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
f908ee29
Commit
f908ee29
authored
Dec 11, 2020
by
Olena Horal-Koretska
Committed by
Natalia Tepluhina
Dec 11, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add grid for rotations display + current day indicator
parent
5726c47d
Changes
28
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
557 additions
and
237 deletions
+557
-237
app/assets/stylesheets/page_bundles/oncall_schedules.scss
app/assets/stylesheets/page_bundles/oncall_schedules.scss
+65
-24
ee/app/assets/javascripts/oncall_schedules/components/delete_schedule_modal.vue
...pts/oncall_schedules/components/delete_schedule_modal.vue
+5
-1
ee/app/assets/javascripts/oncall_schedules/components/edit_schedule_modal.vue
...ripts/oncall_schedules/components/edit_schedule_modal.vue
+5
-1
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedule.vue
...vascripts/oncall_schedules/components/oncall_schedule.vue
+51
-10
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
.../oncall_schedules/components/oncall_schedules_wrapper.vue
+1
-1
ee/app/assets/javascripts/oncall_schedules/components/rotations/add_rotation_modal.vue
...all_schedules/components/rotations/add_rotation_modal.vue
+7
-1
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/current_day_indicator.vue
.../components/schedule/components/current_day_indicator.vue
+21
-0
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_item.vue
...ts/schedule/components/preset_weeks/weeks_header_item.vue
+7
-12
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_sub_item.vue
...chedule/components/preset_weeks/weeks_header_sub_item.vue
+5
-20
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/rotations_list_section.vue
...components/schedule/components/rotations_list_section.vue
+39
-0
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/schedule_timeline_section.vue
...ponents/schedule/components/schedule_timeline_section.vue
+9
-38
ee/app/assets/javascripts/oncall_schedules/components/schedule/constants.js
...scripts/oncall_schedules/components/schedule/constants.js
+0
-4
ee/app/assets/javascripts/oncall_schedules/components/schedule/mixins/common_mixin.js
...call_schedules/components/schedule/mixins/common_mixin.js
+9
-2
ee/spec/frontend/oncall_schedule/__snapshots__/add_schedule_modal_spec.js.snap
...ll_schedule/__snapshots__/add_schedule_modal_spec.js.snap
+1
-1
ee/spec/frontend/oncall_schedule/__snapshots__/edit_schedule_modal_spec.js.snap
...l_schedule/__snapshots__/edit_schedule_modal_spec.js.snap
+1
-1
ee/spec/frontend/oncall_schedule/add_schedule_modal_spec.js
ee/spec/frontend/oncall_schedule/add_schedule_modal_spec.js
+2
-1
ee/spec/frontend/oncall_schedule/delete_schedule_modal_spec.js
...ec/frontend/oncall_schedule/delete_schedule_modal_spec.js
+3
-0
ee/spec/frontend/oncall_schedule/edit_schedule_modal_spec.js
ee/spec/frontend/oncall_schedule/edit_schedule_modal_spec.js
+4
-1
ee/spec/frontend/oncall_schedule/oncall_schedule_spec.js
ee/spec/frontend/oncall_schedule/oncall_schedule_spec.js
+42
-19
ee/spec/frontend/oncall_schedule/rotations/__snapshots__/add_rotation_modal_spec.js.snap
...e/rotations/__snapshots__/add_rotation_modal_spec.js.snap
+1
-1
ee/spec/frontend/oncall_schedule/rotations/add_rotation_modal_spec.js
...tend/oncall_schedule/rotations/add_rotation_modal_spec.js
+5
-0
ee/spec/frontend/oncall_schedule/schedule/components/__snapshots__/rotations_list_section_spec.js.snap
...ponents/__snapshots__/rotations_list_section_spec.js.snap
+34
-0
ee/spec/frontend/oncall_schedule/schedule/components/current_day_indicator_spec.js
...chedule/schedule/components/current_day_indicator_spec.js
+43
-0
ee/spec/frontend/oncall_schedule/schedule/components/preset_weeks/weeks_header_item_spec.js
...chedule/components/preset_weeks/weeks_header_item_spec.js
+38
-46
ee/spec/frontend/oncall_schedule/schedule/components/preset_weeks/weeks_header_sub_item_spec.js
...ule/components/preset_weeks/weeks_header_sub_item_spec.js
+28
-36
ee/spec/frontend/oncall_schedule/schedule/components/rotations_list_section_spec.js
...hedule/schedule/components/rotations_list_section_spec.js
+58
-0
ee/spec/frontend/oncall_schedule/schedule/components/schedule_timeline_section_spec.js
...ule/schedule/components/schedule_timeline_section_spec.js
+3
-17
ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js
...tend/oncall_schedule/schedule/mixins/common_mixin_spec.js
+70
-0
No files found.
app/assets/stylesheets/page_bundles/oncall_schedules.scss
View file @
f908ee29
...
...
@@ -30,8 +30,9 @@
}
//// Copied from roadmaps.scss - adapted for on-call schedules
$header-item-height
:
60px
;
$details-cell-width
:
px-to-rem
(
150px
);
$header-item-height
:
72px
;
$item-height
:
40px
;
$details-cell-width
:
150px
;
$timeline-cell-height
:
32px
;
$timeline-cell-width
:
180px
;
$border-style
:
1px
solid
var
(
--
gray-100
,
$gray-100
);
...
...
@@ -40,7 +41,6 @@ $gradient-gray: rgba(255, 255, 255, 0.001);
$scroll-top-gradient
:
linear-gradient
(
to
bottom
,
$gradient-dark-gray
0%
,
$gradient-gray
100%
);
$scroll-bottom-gradient
:
linear-gradient
(
to
bottom
,
$gradient-gray
0%
,
$gradient-dark-gray
100%
);
$column-right-gradient
:
linear-gradient
(
to
right
,
$gradient-dark-gray
0%
,
$gradient-gray
100%
);
$epic-details-cell-width
:
150px
;
.schedule-shell
{
@include
gl-relative
;
...
...
@@ -55,7 +55,6 @@ $epic-details-cell-width: 150px;
.timeline-section
{
@include
gl-sticky
;
position
:
-
webkit-sticky
;
@include
gl-top-0
;
z-index
:
20
;
...
...
@@ -69,27 +68,15 @@ $epic-details-cell-width: 150px;
.timeline-header-blank
{
@include
gl-sticky
;
position
:
-
webkit-sticky
;
@include
gl-top-0
;
@include
gl-left-0
;
width
:
$details-cell-width
;
z-index
:
2
;
&
:
:
after
{
height
:
$header-item-height
;
@include
gl-content-empty
;
@include
gl-absolute
;
@include
gl-top-0
;
right
:
-
$grid-size
;
width
:
$grid-size
;
@include
gl-pointer-events-none
;
background
:
$column-right-gradient
;
}
}
.timeline-header-item
{
// container size minus left panel width divided by 2 week timeframes
width
:
calc
((
100%
-
#{
$
epic-
details-cell-width
}
)
/
2
);
width
:
calc
((
100%
-
#{
$details-cell-width
}
)
/
2
);
&
:last-of-type
.item-label
{
@include
gl-border-r-0
;
...
...
@@ -110,7 +97,8 @@ $epic-details-cell-width: 150px;
}
.item-label
{
padding
:
$gl-padding-8
$gl-padding
;
@include
gl-py-4
;
@include
gl-pl-7
;
border-right
:
$border-style
;
border-bottom
:
$border-style
;
}
...
...
@@ -124,19 +112,72 @@ $epic-details-cell-width: 150px;
@include
gl-flex-basis-0
;
text-align
:
center
;
font-size
:
$code-font-size
;
line-height
:
1
.5
;
@include
gl-font-base
;
padding
:
2px
0
;
}
}
.current-day-indicator-header
{
@include
gl-absolute
;
@include
gl-bottom-0
;
height
:
$g
l-vert-padding
;
width
:
$g
l-vert-padding
;
height
:
$g
rid-size
;
width
:
$g
rid-size
;
background-color
:
var
(
--
red-500
,
$red-500
);
border-radius
:
50%
;
transform
:
translate
X
(
-3px
);
@include
gl-rounded-full
;
transform
:
translate
(
-50%
,
50%
);
}
}
}
.timeline-section
.timeline-header-blank
,
.list-section
.details-cell
{
&
:
:
after
{
@include
gl-h-full
;
@include
gl-content-empty
;
@include
gl-absolute
;
@include
gl-top-0
;
right
:
-
$grid-size
;
width
:
$grid-size
;
@include
gl-pointer-events-none
;
background
:
$column-right-gradient
;
}
}
.details-cell
,
.timeline-cell
{
@include
float-left
;
height
:
$item-height
;
border-bottom
:
$border-style
;
}
.details-cell
{
@include
gl-sticky
;
@include
gl-left-0
;
width
:
$details-cell-width
;
@include
gl-font-base
;
background-color
:
var
(
--
white
,
$white
);
z-index
:
10
;
}
.timeline-cell
{
@include
gl-relative
;
// width: $timeline-cell-width;
// container size minus left panel width divided by 2 week timeframes
width
:
calc
((
100%
-
#{
$details-cell-width
}
)
/
2
);
@include
gl-bg-transparent
;
border-right
:
$border-style
;
&
:last-child
{
@include
gl-border-r-0
;
}
.current-day-indicator
{
@include
gl-absolute
;
top
:
-1px
;
width
:
$gl-spacing-scale-1
;
height
:
calc
(
100%
+
1px
);
background-color
:
var
(
--
red-500
,
$red-500
);
@include
gl-pointer-events-none
;
transform
:
translateX
(
-50%
);
}
}
ee/app/assets/javascripts/oncall_schedules/components/delete_schedule_modal.vue
View file @
f908ee29
...
...
@@ -25,6 +25,10 @@ export default {
type
:
Object
,
required
:
true
,
},
modalId
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
...
...
@@ -88,7 +92,7 @@ export default {
<
template
>
<gl-modal
ref=
"deleteScheduleModal"
modal-id=
"deleteScheduleModal
"
:modal-id=
"modalId
"
size=
"sm"
:data-testid=
"`delete-schedule-modal-$
{schedule.iid}`"
:title="$options.i18n.deleteSchedule"
...
...
ee/app/assets/javascripts/oncall_schedules/components/edit_schedule_modal.vue
View file @
f908ee29
...
...
@@ -26,6 +26,10 @@ export default {
type
:
Object
,
required
:
true
,
},
modalId
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
...
...
@@ -112,7 +116,7 @@ export default {
<
template
>
<gl-modal
ref=
"updateScheduleModal"
modal-id=
"updateScheduleModal
"
:modal-id=
"modalId
"
size=
"sm"
:data-testid=
"`update-schedule-modal-$
{schedule.iid}`"
:title="$options.i18n.editSchedule"
...
...
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedule.vue
View file @
f908ee29
...
...
@@ -11,18 +11,30 @@ import { s__ } from '~/locale';
import
ScheduleTimelineSection
from
'
./schedule/components/schedule_timeline_section.vue
'
;
import
DeleteScheduleModal
from
'
./delete_schedule_modal.vue
'
;
import
EditScheduleModal
from
'
./edit_schedule_modal.vue
'
;
import
AddRotationModal
from
'
./rotations/add_rotation_modal.vue
'
;
import
{
getTimeframeForWeeksView
}
from
'
./schedule/utils
'
;
import
{
PRESET_TYPES
}
from
'
./schedule/constants
'
;
import
{
getFormattedTimezone
}
from
'
../utils/common_utils
'
;
import
RotationsListSection
from
'
./schedule/components/rotations_list_section.vue
'
;
export
const
i18n
=
{
scheduleForTz
:
s__
(
'
OnCallSchedules|On-call schedule for the %{tzShort}
'
),
editScheduleLabel
:
s__
(
'
OnCallSchedules|Edit schedule
'
),
deleteScheduleLabel
:
s__
(
'
OnCallSchedules|Delete schedule
'
),
rotationTitle
:
s__
(
'
OnCallSchedules|Rotations
'
),
addARotation
:
s__
(
'
OnCallSchedules|Add a rotation
'
),
};
export
const
addRotationModalId
=
'
addRotationModal
'
;
export
const
editScheduleModalId
=
'
editScheduleModal
'
;
export
const
deleteScheduleModalId
=
'
deleteScheduleModal
'
;
export
default
{
i18n
,
addRotationModalId
,
editScheduleModalId
,
deleteScheduleModalId
,
presetType
:
PRESET_TYPES
.
WEEKS
,
inject
:
[
'
timezones
'
],
components
:
{
...
...
@@ -33,6 +45,8 @@ export default {
GlButton
,
DeleteScheduleModal
,
EditScheduleModal
,
AddRotationModal
,
RotationsListSection
,
},
directives
:
{
GlModal
:
GlModalDirective
,
...
...
@@ -43,6 +57,11 @@ export default {
type
:
Object
,
required
:
true
,
},
rotations
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
},
computed
:
{
tzLong
()
{
...
...
@@ -60,18 +79,21 @@ export default {
<div>
<gl-card>
<template
#header
>
<div
class=
"gl-display-flex gl-justify-content-space-between gl-m-0"
>
<div
class=
"gl-display-flex gl-justify-content-space-between gl-m-0"
data-testid=
"scheduleHeader"
>
<span
class=
"gl-font-weight-bold gl-font-lg"
>
{{
schedule
.
name
}}
</span>
<gl-button-group>
<gl-button
v-gl-modal
.
updateScheduleModal
v-gl-modal
=
"$options.editScheduleModalId"
v-gl-tooltip
:title=
"$options.i18n.editScheduleLabel"
icon=
"pencil"
:aria-label=
"$options.i18n.editScheduleLabel"
/>
<gl-button
v-gl-modal
.
deleteScheduleModal
v-gl-modal
=
"$options.deleteScheduleModalId"
v-gl-tooltip
:title=
"$options.i18n.deleteScheduleLabel"
icon=
"remove"
...
...
@@ -80,19 +102,38 @@ export default {
</gl-button-group>
</div>
</
template
>
<p
class=
"gl-text-gray-500 gl-mb-5"
>
<p
class=
"gl-text-gray-500 gl-mb-5"
data-testid=
"scheduleBody"
>
<gl-sprintf
:message=
"$options.i18n.scheduleForTz"
>
<
template
#tzShort
>
{{
schedule
.
timezone
}}
</
template
>
</gl-sprintf>
| {{ tzLong }}
</p>
<div
class=
"schedule-shell"
>
<schedule-timeline-section
:preset-type=
"$options.presetType"
:timeframe=
"timeframe"
/>
</div>
<gl-card
header-class=
"gl-bg-transparent"
>
<
template
#header
>
<div
class=
"gl-display-flex gl-justify-content-space-between"
data-testid=
"rotationsHeader"
>
<h6
class=
"gl-m-0"
>
{{
$options
.
i18n
.
rotationTitle
}}
</h6>
<gl-button
v-gl-modal=
"$options.addRotationModalId"
variant=
"link"
>
{{
$options
.
i18n
.
addARotation
}}
</gl-button>
</div>
</
template
>
<div
class=
"schedule-shell"
data-testid=
"rotationsBody"
>
<schedule-timeline-section
:preset-type=
"$options.presetType"
:timeframe=
"timeframe"
/>
<rotations-list-section
:preset-type=
"$options.presetType"
:rotations=
"rotations"
:timeframe=
"timeframe"
/>
</div>
</gl-card>
</gl-card>
<delete-schedule-modal
:schedule=
"schedule"
/>
<edit-schedule-modal
:schedule=
"schedule"
/>
<delete-schedule-modal
:schedule=
"schedule"
:modal-id=
"$options.deleteScheduleModalId"
/>
<edit-schedule-modal
:schedule=
"schedule"
:modal-id=
"$options.editScheduleModalId"
/>
<add-rotation-modal
:modal-id=
"$options.addRotationModalId"
/>
</div>
</template>
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
View file @
f908ee29
...
...
@@ -7,7 +7,7 @@ import { s__ } from '~/locale';
import
getOncallSchedulesQuery
from
'
../graphql/queries/get_oncall_schedules.query.graphql
'
;
import
{
fetchPolicies
}
from
'
~/lib/graphql
'
;
const
addScheduleModalId
=
'
addScheduleModal
'
;
export
const
addScheduleModalId
=
'
addScheduleModal
'
;
export
const
i18n
=
{
title
:
s__
(
'
OnCallSchedules|On-call schedule
'
),
...
...
ee/app/assets/javascripts/oncall_schedules/components/rotations/add_rotation_modal.vue
View file @
f908ee29
...
...
@@ -60,6 +60,12 @@ export default {
GlAvatarLabeled
,
GlAlert
,
},
props
:
{
modalId
:
{
type
:
String
,
required
:
true
,
},
},
apollo
:
{
participants
:
{
query
:
usersSearchQuery
,
...
...
@@ -169,7 +175,7 @@ export default {
<
template
>
<gl-modal
ref=
"createScheduleRotationModal"
modal-id=
"create-schedule-rotation-modal
"
:modal-id=
"modalId
"
size=
"sm"
:title=
"$options.i18n.addRotation"
:action-primary=
"actionsProps.primary"
...
...
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/current_day_indicator.vue
0 → 100644
View file @
f908ee29
<
script
>
import
CommonMixin
from
'
../mixins/common_mixin
'
;
export
default
{
mixins
:
[
CommonMixin
],
props
:
{
presetType
:
{
type
:
String
,
required
:
true
,
},
timeframeItem
:
{
type
:
[
Date
,
Object
],
required
:
true
,
},
},
};
</
script
>
<
template
>
<span
v-if=
"hasToday"
:style=
"getIndicatorStyles()"
class=
"current-day-indicator"
></span>
</
template
>
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_item.vue
View file @
f908ee29
<
script
>
import
{
monthInWords
}
from
'
~/lib/utils/datetime_utility
'
;
import
WeeksHeaderSubItem
from
'
./weeks_header_sub_item.vue
'
;
import
CommonMixin
from
'
../../mixins/common_mixin
'
;
export
default
{
components
:
{
WeeksHeaderSubItem
,
},
mixins
:
[
CommonMixin
],
props
:
{
timeframeIndex
:
{
type
:
Number
,
...
...
@@ -21,14 +22,6 @@ export default {
required
:
true
,
},
},
data
()
{
const
currentDate
=
new
Date
();
currentDate
.
setHours
(
0
,
0
,
0
,
0
);
return
{
currentDate
,
};
},
computed
:
{
lastDayOfCurrentWeek
()
{
const
lastDayOfCurrentWeek
=
new
Date
(
this
.
timeframeItem
.
getTime
());
...
...
@@ -50,7 +43,7 @@ export default {
return
`
${
monthInWords
(
this
.
timeframeItem
,
true
)}
${
timeframeItemDate
}
`
;
},
timelineHeaderClass
()
{
const
currentDateTime
=
this
.
currentDate
.
getTime
();
const
currentDateTime
=
this
.
$options
.
currentDate
.
getTime
();
const
lastDayOfCurrentWeekTime
=
this
.
lastDayOfCurrentWeek
.
getTime
();
if
(
...
...
@@ -68,7 +61,9 @@ export default {
<
template
>
<span
class=
"timeline-header-item"
>
<div
:class=
"timelineHeaderClass"
class=
"item-label"
>
{{
timelineHeaderLabel
}}
</div>
<weeks-header-sub-item
:timeframe-item=
"timeframeItem"
:current-date=
"currentDate"
/>
<div
:class=
"timelineHeaderClass"
class=
"item-label"
data-testid=
"timeline-header-label"
>
{{
timelineHeaderLabel
}}
</div>
<weeks-header-sub-item
:timeframe-item=
"timeframeItem"
/>
</span>
</
template
>
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_sub_item.vue
View file @
f908ee29
<
script
>
import
{
PRESET_TYPES
}
from
'
../../constants
'
;
import
CommonMixin
from
'
../../mixins/common_mixin
'
;
export
default
{
mixins
:
[
CommonMixin
],
props
:
{
currentDate
:
{
type
:
Date
,
required
:
true
,
},
timeframeItem
:
{
type
:
Date
,
required
:
true
,
},
},
data
()
{
return
{
presetType
:
PRESET_TYPES
.
WEEKS
,
indicatorStyle
:
{},
};
},
computed
:
{
headerSubItems
()
{
const
timeframeItem
=
new
Date
(
this
.
timeframeItem
.
getTime
());
...
...
@@ -37,17 +26,12 @@ export default {
return
headerSubItems
;
},
},
mounted
()
{
this
.
$nextTick
(()
=>
{
this
.
indicatorStyle
=
this
.
getIndicatorStyles
();
});
},
methods
:
{
getSubItemValueClass
(
subItem
)
{
// Show dark color text only for current & upcoming dates
if
(
subItem
.
getTime
()
===
this
.
currentDate
.
getTime
())
{
if
(
subItem
.
getTime
()
===
this
.
$options
.
currentDate
.
getTime
())
{
return
'
label-dark label-bold
'
;
}
else
if
(
subItem
>
this
.
currentDate
)
{
}
else
if
(
subItem
>
this
.
$options
.
currentDate
)
{
return
'
label-dark
'
;
}
return
''
;
...
...
@@ -63,12 +47,13 @@ export default {
:key=
"index"
:class=
"getSubItemValueClass(subItem)"
class=
"sublabel-value"
data-testid=
"sublabel-value"
>
{{
subItem
.
getDate
()
}}
</span
>
<span
v-if=
"hasToday"
:style=
"
indicatorStyle
"
class=
"current-day-indicator-header preset-weeks
gl-absolute
"
:style=
"
getIndicatorStyles()
"
class=
"current-day-indicator-header preset-weeks"
></span>
</div>
</
template
>
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/rotations_list_section.vue
0 → 100644
View file @
f908ee29
<
script
>
import
CurrentDayIndicator
from
'
./current_day_indicator.vue
'
;
export
default
{
components
:
{
CurrentDayIndicator
,
},
props
:
{
presetType
:
{
type
:
String
,
required
:
true
,
},
rotations
:
{
type
:
Array
,
required
:
true
,
},
timeframe
:
{
type
:
Array
,
required
:
true
,
},
},
};
</
script
>
<
template
>
<div
class=
"list-section"
>
<div
class=
"list-item list-item-empty clearfix"
>
<span
class=
"details-cell"
></span>
<span
v-for=
"(timeframeItem, index) in timeframe"
:key=
"index"
class=
"timeline-cell"
data-testid=
"timelineCell"
>
<current-day-indicator
:preset-type=
"presetType"
:timeframe-item=
"timeframeItem"
/>
</span>
</div>
</div>
</
template
>
ee/app/assets/javascripts/oncall_schedules/components/schedule/components/schedule_timeline_section.vue
View file @
f908ee29
<
script
>
import
{
GlCard
,
GlButton
,
GlModalDirective
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
WeeksHeaderItem
from
'
./preset_weeks/weeks_header_item.vue
'
;
import
AddRotationModal
from
'
../../rotations/add_rotation_modal.vue
'
;
export
const
i18n
=
{
rotationTitle
:
s__
(
'
OnCallSchedules|Rotations
'
),
addARotation
:
s__
(
'
OnCallSchedules|Add a rotation
'
),
};
export
default
{
i18n
,
components
:
{
GlButton
,
GlCard
,
WeeksHeaderItem
,
AddRotationModal
,
},
directives
:
{
GlModal
:
GlModalDirective
,
},
props
:
{
presetType
:
{
...
...
@@ -34,28 +19,14 @@ export default {
</
script
>
<
template
>
<div>
<gl-card
header-class=
"gl-bg-transparent"
>
<template
#header
>
<div
class=
"gl-display-flex gl-justify-content-space-between"
>
<h6
class=
"gl-m-0"
>
{{
$options
.
i18n
.
rotationTitle
}}
</h6>
<gl-button
v-gl-modal=
"'create-schedule-rotation-modal'"
variant=
"link"
>
{{
$options
.
i18n
.
addARotation
}}
</gl-button>
</div>
</
template
>
<div
class=
"timeline-section clearfix"
>
<span
class=
"timeline-header-blank"
></span>
<weeks-header-item
v-for=
"(timeframeItem, index) in timeframe"
:key=
"index"
:timeframe-index=
"index"
:timeframe-item=
"timeframeItem"
:timeframe=
"timeframe"
/>
</div>
</gl-card>
<add-rotation-modal
/>
<div
class=
"timeline-section clearfix"
>
<span
class=
"timeline-header-blank"
></span>
<weeks-header-item
v-for=
"(timeframeItem, index) in timeframe"
:key=
"index"
:timeframe-index=
"index"
:timeframe-item=
"timeframeItem"
:timeframe=
"timeframe"
/>
</div>
</
template
>
ee/app/assets/javascripts/oncall_schedules/components/schedule/constants.js
View file @
f908ee29
...
...
@@ -9,7 +9,3 @@ export const PRESET_DEFAULTS = {
TIMEFRAME_LENGTH
:
2
,
},
};
export
const
PAST_DATE
=
new
Date
(
new
Date
().
getFullYear
()
-
100
,
0
,
1
);
export
const
FUTURE_DATE
=
new
Date
(
new
Date
().
getFullYear
()
+
100
,
0
,
1
);
ee/app/assets/javascripts/oncall_schedules/components/schedule/mixins/common_mixin.js
View file @
f908ee29
import
{
DAYS_IN_WEEK
}
from
'
../constants
'
;
export
default
{
currentDate
:
null
,
computed
:
{
hasToday
()
{
const
timeframeItem
=
new
Date
(
this
.
timeframeItem
.
getTime
());
...
...
@@ -16,11 +17,17 @@ export default {
);
return
(
this
.
currentDate
.
getTime
()
>=
headerSubItems
[
0
].
getTime
()
&&
this
.
currentDate
.
getTime
()
<=
headerSubItems
[
headerSubItems
.
length
-
1
].
getTime
()
this
.
$options
.
currentDate
.
getTime
()
>=
headerSubItems
[
0
].
getTime
()
&&
this
.
$options
.
currentDate
.
getTime
()
<=
headerSubItems
[
headerSubItems
.
length
-
1
].
getTime
()
);
},
},
beforeCreate
()
{
const
currentDate
=
new
Date
();
currentDate
.
setHours
(
0
,
0
,
0
,
0
);
this
.
$options
.
currentDate
=
currentDate
;
},
methods
:
{
getIndicatorStyles
()
{
// as we start schedule scale from the current date the indicator will always be on the first date. So we find
...
...
ee/spec/frontend/oncall_schedule/__snapshots__/add_schedule_modal_spec.js.snap
View file @
f908ee29
...
...
@@ -5,7 +5,7 @@ exports[`AddScheduleModal renders modal layout 1`] = `
actioncancel="[object Object]"
actionprimary="[object Object]"
modalclass=""
modalid="
modalId
"
modalid="
addScheduleModal
"
size="sm"
title="Add schedule"
titletag="h4"
...
...
ee/spec/frontend/oncall_schedule/__snapshots__/edit_schedule_modal_spec.js.snap
View file @
f908ee29
...
...
@@ -6,7 +6,7 @@ exports[`UpdateScheduleModal renders update schedule modal layout 1`] = `
actionprimary="[object Object]"
data-testid="update-schedule-modal-37"
modalclass=""
modalid="
update
ScheduleModal"
modalid="
edit
ScheduleModal"
size="sm"
title="Edit schedule"
titletag="h4"
...
...
ee/spec/frontend/oncall_schedule/add_schedule_modal_spec.js
View file @
f908ee29
...
...
@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
AddScheduleModal
from
'
ee/oncall_schedules/components/add_schedule_modal.vue
'
;
import
{
addScheduleModalId
}
from
'
ee/oncall_schedules/components/oncall_schedules_wrapper
'
;
import
{
getOncallSchedulesQueryResponse
}
from
'
./mocks/apollo_mock
'
;
import
mockTimezones
from
'
./mocks/mockTimezones.json
'
;
...
...
@@ -22,7 +23,7 @@ describe('AddScheduleModal', () => {
};
},
propsData
:
{
modalId
:
'
modalId
'
,
modalId
:
addScheduleModalId
,
...
props
,
},
provide
:
{
...
...
ee/spec/frontend/oncall_schedule/delete_schedule_modal_spec.js
View file @
f908ee29
...
...
@@ -8,6 +8,7 @@ import destroyOncallScheduleMutation from 'ee/oncall_schedules/graphql/mutations
import
DeleteScheduleModal
,
{
i18n
,
}
from
'
ee/oncall_schedules/components/delete_schedule_modal.vue
'
;
import
{
deleteScheduleModalId
}
from
'
ee/oncall_schedules/components/oncall_schedule
'
;
import
{
getOncallSchedulesQueryResponse
,
destroyScheduleResponse
,
...
...
@@ -53,6 +54,7 @@ describe('DeleteScheduleModal', () => {
};
},
propsData
:
{
modalId
:
deleteScheduleModalId
,
schedule
,
...
props
,
},
...
...
@@ -95,6 +97,7 @@ describe('DeleteScheduleModal', () => {
apolloProvider
:
fakeApollo
,
propsData
:
{
schedule
,
modalId
:
deleteScheduleModalId
,
},
provide
:
{
projectPath
,
...
...
ee/spec/frontend/oncall_schedule/edit_schedule_modal_spec.js
View file @
f908ee29
...
...
@@ -6,6 +6,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import
getOncallSchedulesQuery
from
'
ee/oncall_schedules/graphql/queries/get_oncall_schedules.query.graphql
'
;
import
updateOncallScheduleMutation
from
'
ee/oncall_schedules/graphql/mutations/update_oncall_schedule.mutation.graphql
'
;
import
UpdateScheduleModal
,
{
i18n
}
from
'
ee/oncall_schedules/components/edit_schedule_modal.vue
'
;
import
{
editScheduleModalId
}
from
'
ee/oncall_schedules/components/oncall_schedule
'
;
import
{
getOncallSchedulesQueryResponse
,
updateScheduleResponse
,
...
...
@@ -52,6 +53,7 @@ describe('UpdateScheduleModal', () => {
};
},
propsData
:
{
modalId
:
editScheduleModalId
,
schedule
,
...
props
,
},
...
...
@@ -98,6 +100,7 @@ describe('UpdateScheduleModal', () => {
};
},
propsData
:
{
modalId
:
editScheduleModalId
,
schedule
,
},
provide
:
{
...
...
@@ -122,7 +125,7 @@ describe('UpdateScheduleModal', () => {
describe
(
'
renders update modal with the correct schedule information
'
,
()
=>
{
it
(
'
renders name of correct modal id
'
,
()
=>
{
expect
(
findModal
().
attributes
(
'
modalid
'
)).
toBe
(
'
updateScheduleModal
'
);
expect
(
findModal
().
attributes
(
'
modalid
'
)).
toBe
(
editScheduleModalId
);
});
it
(
'
renders name of schedule to update
'
,
()
=>
{
...
...
ee/spec/frontend/oncall_schedule/oncall_schedule_spec.js
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlCard
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
GlCard
,
GlSprintf
,
GlButton
}
from
'
@gitlab/ui
'
;
import
OnCallSchedule
,
{
i18n
}
from
'
ee/oncall_schedules/components/oncall_schedule.vue
'
;
import
ScheduleTimelineSection
from
'
ee/oncall_schedules/components/schedule/components/schedule_timeline_section.vue
'
;
import
RotationsListSection
from
'
ee/oncall_schedules/components/schedule/components/rotations_list_section.vue
'
;
import
*
as
utils
from
'
ee/oncall_schedules/components/schedule/utils
'
;
import
*
as
commonUtils
from
'
ee/oncall_schedules/utils/common_utils
'
;
import
{
PRESET_TYPES
}
from
'
ee/oncall_schedules/components/schedule/constants
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
mockTimezones
from
'
./mocks/mockTimezones.json
'
;
describe
(
'
On-call schedule
'
,
()
=>
{
...
...
@@ -21,18 +23,20 @@ describe('On-call schedule', () => {
const
formattedTimezone
=
'
(UTC-09:00) AKST Alaska
'
;
function
mountComponent
({
schedule
}
=
{})
{
wrapper
=
shallowMount
(
OnCallSchedule
,
{
propsData
:
{
schedule
,
},
provide
:
{
timezones
:
mockTimezones
,
},
stubs
:
{
GlCard
,
GlSprintf
,
},
});
wrapper
=
extendedWrapper
(
shallowMount
(
OnCallSchedule
,
{
propsData
:
{
schedule
,
},
provide
:
{
timezones
:
mockTimezones
,
},
stubs
:
{
GlCard
,
GlSprintf
,
},
}),
);
}
beforeEach
(()
=>
{
...
...
@@ -46,23 +50,32 @@ describe('On-call schedule', () => {
wrapper
=
null
;
});
const
findCardHeader
=
()
=>
wrapper
.
find
(
'
.gl-card-header
'
);
const
findCardDescription
=
()
=>
wrapper
.
find
(
'
.gl-card-body
'
);
const
findScheduleTimeline
=
()
=>
findCardDescription
().
find
(
ScheduleTimelineSection
);
const
findScheduleHeader
=
()
=>
wrapper
.
findByTestId
(
'
scheduleHeader
'
);
const
findRotationsHeader
=
()
=>
wrapper
.
findByTestId
(
'
rotationsHeader
'
);
const
findSchedule
=
()
=>
wrapper
.
findByTestId
(
'
scheduleBody
'
);
const
findRotations
=
()
=>
wrapper
.
findByTestId
(
'
rotationsBody
'
);
const
findAddRotationsBtn
=
()
=>
findRotationsHeader
().
find
(
GlButton
);
const
findScheduleTimeline
=
()
=>
findRotations
().
find
(
ScheduleTimelineSection
);
const
findRotationsList
=
()
=>
findRotations
().
find
(
RotationsListSection
);
it
(
'
shows schedule title
'
,
()
=>
{
expect
(
find
Card
Header
().
text
()).
toBe
(
mockSchedule
.
name
);
expect
(
find
Schedule
Header
().
text
()).
toBe
(
mockSchedule
.
name
);
});
it
(
'
shows timezone info
'
,
()
=>
{
const
shortTz
=
i18n
.
scheduleForTz
.
replace
(
'
%{tzShort}
'
,
lastTz
.
identifier
);
const
longTz
=
formattedTimezone
;
const
description
=
find
CardDescription
().
text
();
const
description
=
find
Schedule
().
text
();
expect
(
description
).
toContain
(
shortTz
);
expect
(
description
).
toContain
(
longTz
);
});
it
(
'
renders ScheduleShell
'
,
()
=>
{
it
(
'
renders rotations header
'
,
()
=>
{
expect
(
findRotationsHeader
().
text
()).
toContain
(
i18n
.
rotationTitle
);
expect
(
findAddRotationsBtn
().
text
()).
toContain
(
i18n
.
addARotation
);
});
it
(
'
renders schedule timeline
'
,
()
=>
{
const
timeline
=
findScheduleTimeline
();
expect
(
timeline
.
exists
()).
toBe
(
true
);
expect
(
timeline
.
props
()).
toEqual
({
...
...
@@ -70,4 +83,14 @@ describe('On-call schedule', () => {
timeframe
:
mockWeeksTimeFrame
,
});
});
it
(
'
renders rotations list
'
,
()
=>
{
const
rotationsList
=
findRotationsList
();
expect
(
rotationsList
.
exists
()).
toBe
(
true
);
expect
(
rotationsList
.
props
()).
toEqual
({
presetType
:
PRESET_TYPES
.
WEEKS
,
timeframe
:
mockWeeksTimeFrame
,
rotations
:
expect
.
any
(
Array
),
});
});
});
ee/spec/frontend/oncall_schedule/rotations/__snapshots__/add_rotation_modal_spec.js.snap
View file @
f908ee29
...
...
@@ -5,7 +5,7 @@ exports[`AddRotationModal renders rotation modal layout 1`] = `
actioncancel="[object Object]"
actionprimary="[object Object]"
modalclass=""
modalid="
create-schedule-rotation-m
odal"
modalid="
addRotationM
odal"
size="sm"
title="Add rotation"
titletag="h4"
...
...
ee/spec/frontend/oncall_schedule/rotations/add_rotation_modal_spec.js
View file @
f908ee29
...
...
@@ -3,6 +3,7 @@ import createMockApollo from 'jest/helpers/mock_apollo_helper';
import
VueApollo
from
'
vue-apollo
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
GlDropdownItem
,
GlModal
,
GlAlert
,
GlTokenSelector
}
from
'
@gitlab/ui
'
;
import
{
addRotationModalId
}
from
'
ee/oncall_schedules/components/oncall_schedule
'
;
import
AddRotationModal
from
'
ee/oncall_schedules/components/rotations/add_rotation_modal.vue
'
;
// import createOncallScheduleRotationMutation from 'ee/oncall_schedules/graphql/create_oncall_schedule_rotation.mutation.graphql';
import
usersSearchQuery
from
'
~/graphql_shared/queries/users_search.query.graphql
'
;
...
...
@@ -34,6 +35,7 @@ describe('AddRotationModal', () => {
};
},
propsData
:
{
modalId
:
addRotationModalId
,
...
props
,
},
provide
:
{
...
...
@@ -58,6 +60,9 @@ describe('AddRotationModal', () => {
wrapper
=
shallowMount
(
AddRotationModal
,
{
localVue
,
propsData
:
{
modalId
:
addRotationModalId
,
},
apolloProvider
:
fakeApollo
,
data
()
{
return
{
...
...
ee/spec/frontend/oncall_schedule/schedule/components/__snapshots__/rotations_list_section_spec.js.snap
0 → 100644
View file @
f908ee29
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RotationsListSectionComponent renders component layout 1`] = `
<div
class="list-section"
>
<div
class="list-item list-item-empty clearfix"
>
<span
class="details-cell"
/>
<span
class="timeline-cell"
data-testid="timelineCell"
>
<current-day-indicator-stub
presettype="WEEKS"
timeframeitem="Mon Jan 01 2018 00:00:00 GMT+0000 (Greenwich Mean Time)"
/>
</span>
<span
class="timeline-cell"
data-testid="timelineCell"
>
<current-day-indicator-stub
presettype="WEEKS"
timeframeitem="Mon Jan 08 2018 00:00:00 GMT+0000 (Greenwich Mean Time)"
/>
</span>
</div>
</div>
`;
ee/spec/frontend/oncall_schedule/schedule/components/current_day_indicator_spec.js
0 → 100644
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
CurrentDayIndicator
from
'
ee/oncall_schedules/components/schedule/components/current_day_indicator.vue
'
;
import
{
PRESET_TYPES
,
DAYS_IN_WEEK
}
from
'
ee/oncall_schedules/components/schedule/constants
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
describe
(
'
CurrentDayIndicator
'
,
()
=>
{
let
wrapper
;
// January 3rd, 2018 - current date (faked)
useFakeDate
(
2018
,
0
,
3
);
// January 1st, 2018 is the first day of the week-long timeframe
// so as long as current date (faked January 3rd, 2018) is within week timeframe
// current indicator will be rendered
const
mockTimeframeInitialDate
=
new
Date
(
2018
,
0
,
1
);
function
mountComponent
()
{
wrapper
=
shallowMount
(
CurrentDayIndicator
,
{
propsData
:
{
presetType
:
PRESET_TYPES
.
WEEKS
,
timeframeItem
:
mockTimeframeInitialDate
,
},
});
}
beforeEach
(()
=>
{
mountComponent
();
});
afterEach
(()
=>
{
if
(
wrapper
)
{
wrapper
.
destroy
();
wrapper
=
null
;
}
});
it
(
'
renders span element containing class `current-day-indicator`
'
,
()
=>
{
expect
(
wrapper
.
classes
(
'
current-day-indicator
'
)).
toBe
(
true
);
});
it
(
'
sets correct styles
'
,
async
()
=>
{
const
left
=
100
/
DAYS_IN_WEEK
/
2
;
expect
(
wrapper
.
attributes
(
'
style
'
)).
toBe
(
`left:
${
left
}
%;`
);
});
});
ee/spec/frontend/oncall_schedule/schedule/components/preset_weeks/weeks_header_item_spec.js
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
WeeksHeaderItemComponent
from
'
ee/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_item.vue
'
;
import
{
getTimeframeForWeeksView
}
from
'
ee/oncall_schedules/components/schedule/utils
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
describe
(
'
WeeksHeaderItemComponent
'
,
()
=>
{
let
wrapper
;
// January 3rd, 2018 - current date (faked)
useFakeDate
(
2018
,
0
,
3
);
const
mockTimeframeIndex
=
0
;
const
mockTimeframeInitialDate
=
new
Date
(
2018
,
0
,
1
);
const
mockTimeframeWeeks
=
getTimeframeForWeeksView
(
mockTimeframeInitialDate
);
...
...
@@ -12,7 +15,7 @@ describe('WeeksHeaderItemComponent', () => {
timeframeIndex
=
mockTimeframeIndex
,
timeframeItem
=
mockTimeframeWeeks
[
mockTimeframeIndex
],
timeframe
=
mockTimeframeWeeks
,
})
{
}
=
{}
)
{
wrapper
=
shallowMount
(
WeeksHeaderItemComponent
,
{
propsData
:
{
timeframeIndex
,
...
...
@@ -23,7 +26,7 @@ describe('WeeksHeaderItemComponent', () => {
}
beforeEach
(()
=>
{
mountComponent
(
{}
);
mountComponent
();
});
afterEach
(()
=>
{
...
...
@@ -33,66 +36,55 @@ describe('WeeksHeaderItemComponent', () => {
}
});
describe
(
'
data
'
,
()
=>
{
it
(
'
returns default data props
'
,
()
=>
{
const
currentDate
=
new
Date
();
expect
(
wrapper
.
vm
.
currentDate
.
getDate
()).
toBe
(
currentDate
.
getDate
());
const
findHeaderLabel
=
()
=>
wrapper
.
find
(
'
[data-testid="timeline-header-label"]
'
);
describe
(
'
lastDayOfCurrentWeek
'
,
()
=>
{
it
(
'
returns date object representing last day of the week as set in `timeframeItem`
'
,
()
=>
{
expect
(
wrapper
.
vm
.
lastDayOfCurrentWeek
.
getDate
()).
toBe
(
mockTimeframeWeeks
[
mockTimeframeIndex
].
getDate
()
+
7
,
);
});
});
describe
(
'
computed
'
,
()
=>
{
describe
(
'
lastDayOfCurrentWeek
'
,
()
=>
{
it
(
'
returns date object representing last day of the week as set in `timeframeItem`
'
,
()
=>
{
expect
(
wrapper
.
vm
.
lastDayOfCurrentWeek
.
getDate
()).
toBe
(
mockTimeframeWeeks
[
mockTimeframeIndex
].
getDate
()
+
7
,
);
});
describe
(
'
timelineHeaderLabel
'
,
()
=>
{
it
(
'
returns string containing Year, Month and Date for the first timeframe item in the entire timeframe
'
,
()
=>
{
expect
(
findHeaderLabel
().
text
()).
toBe
(
'
2018 Jan 1
'
);
});
describe
(
'
timelineHeaderLabel
'
,
()
=>
{
it
(
'
returns string containing Year, Month and Date for the first timeframe item in the entire timeframe
'
,
()
=>
{
expect
(
wrapper
.
vm
.
timelineHeaderLabel
).
toBe
(
'
2018 Jan 1
'
);
});
it
(
'
returns string containing Year, Month and Date for timeframe item when it is first week of the year
'
,
()
=>
{
mountComponent
({
timeframeIndex
:
3
,
timeframeItem
:
new
Date
(
2019
,
0
,
6
),
});
expect
(
wrapper
.
vm
.
timelineHeaderLabel
).
toBe
(
'
2019 Jan 6
'
);
it
(
'
returns string containing Year, Month and Date for timeframe item that is the first week of the year
'
,
()
=>
{
mountComponent
({
timeframeIndex
:
3
,
timeframeItem
:
new
Date
(
2019
,
0
,
6
),
});
it
(
'
returns string containing only Month and Date timeframe item when it is somewhere in the middle of timeframe
'
,
()
=>
{
mountComponent
({
timeframeIndex
:
mockTimeframeIndex
+
1
,
timeframeItem
:
mockTimeframeWeeks
[
mockTimeframeIndex
+
1
],
});
expect
(
wrapper
.
vm
.
timelineHeaderLabel
).
toBe
(
'
Jan 8
'
);
});
expect
(
findHeaderLabel
().
text
()).
toBe
(
'
2019 Jan 6
'
);
});
describe
(
'
timelineHeaderClass
'
,
()
=>
{
it
(
'
returns empty string when timeframeItem week is less than current week
'
,
()
=>
{
expect
(
wrapper
.
vm
.
timelineHeaderClass
).
toBe
(
''
);
it
(
'
returns string containing only Month and Date when timeframe item is somewhere in the middle of the timeframe
'
,
()
=>
{
mountComponent
({
timeframeIndex
:
mockTimeframeIndex
+
1
,
timeframeItem
:
mockTimeframeWeeks
[
mockTimeframeIndex
+
1
],
});
it
(
'
returns string containing `label-dark label-bold` when current week is same as timeframeItem week
'
,
()
=>
{
wrapper
.
setData
({
currentDate
:
mockTimeframeWeeks
[
mockTimeframeIndex
]
});
expect
(
wrapper
.
vm
.
timelineHeaderClass
).
toBe
(
'
label-dark label-bold
'
);
});
expect
(
findHeaderLabel
().
text
()).
toBe
(
'
Jan 8
'
);
});
});
describe
(
'
template
'
,
()
=>
{
it
(
'
renders component container element with class `timeline-header-item`
'
,
()
=>
{
expect
(
wrapper
.
classes
()).
toContain
(
'
timeline-header-item
'
);
describe
(
'
timelineHeaderClass
'
,
()
=>
{
it
(
'
returns empty string when timeframeItem week is outside of current week
'
,
()
=>
{
mountComponent
({
timeframeIndex
:
3
,
timeframeItem
:
new
Date
(
2017
,
0
,
6
),
});
expect
(
findHeaderLabel
().
classes
()).
not
.
toEqual
(
expect
.
arrayContaining
([
'
label-dark
'
,
'
label-bold
'
]),
);
});
it
(
'
renders item label element class `item-label` and value as `timelineHeaderLabel`
'
,
()
=>
{
expect
(
wrapper
.
find
(
'
.item-label
'
).
text
()).
toBe
(
'
2018 Jan 1
'
);
it
(
'
returns string containing `label-dark label-bold` when current week is same as timeframeItem week
'
,
()
=>
{
expect
(
findHeaderLabel
().
classes
()).
toEqual
(
expect
.
arrayContaining
([
'
label-dark
'
,
'
label-bold
'
]),
);
});
});
});
ee/spec/frontend/oncall_schedule/schedule/components/preset_weeks/weeks_header_sub_item_spec.js
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
WeeksHeaderSubItemComponent
from
'
ee/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_sub_item.vue
'
;
import
{
getTimeframeForWeeksView
}
from
'
ee/oncall_schedules/components/schedule/utils
'
;
import
{
PRESET_TYPES
}
from
'
ee/oncall_schedules/components/schedule/constants
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
describe
(
'
Month
sHeaderSubItemComponent
'
,
()
=>
{
describe
(
'
Week
sHeaderSubItemComponent
'
,
()
=>
{
let
wrapper
;
// January 3rd, 2018 - current date (faked)
useFakeDate
(
2018
,
0
,
3
);
const
mockTimeframeInitialDate
=
new
Date
(
2018
,
0
,
1
);
const
mockTimeframeWeeks
=
getTimeframeForWeeksView
(
mockTimeframeInitialDate
);
function
mountComponent
({
currentDate
=
mockTimeframeWeeks
[
0
],
timeframeItem
=
mockTimeframeWeeks
[
0
],
})
{
function
mountComponent
({
timeframeItem
=
mockTimeframeWeeks
[
0
]
})
{
wrapper
=
shallowMount
(
WeeksHeaderSubItemComponent
,
{
propsData
:
{
currentDate
,
timeframeItem
,
},
});
...
...
@@ -31,12 +29,7 @@ describe('MonthsHeaderSubItemComponent', () => {
}
});
describe
(
'
data
'
,
()
=>
{
it
(
'
initializes `presetType` and `indicatorStyles` data props
'
,
()
=>
{
expect
(
wrapper
.
vm
.
presetType
).
toBe
(
PRESET_TYPES
.
WEEKS
);
expect
(
wrapper
.
vm
.
indicatorStyle
).
toBeDefined
();
});
});
const
findSublabelValues
=
()
=>
wrapper
.
findAll
(
'
[data-testid="sublabel-value"]
'
);
describe
(
'
computed
'
,
()
=>
{
describe
(
'
headerSubItems
'
,
()
=>
{
...
...
@@ -50,29 +43,6 @@ describe('MonthsHeaderSubItemComponent', () => {
});
});
describe
(
'
methods
'
,
()
=>
{
describe
(
'
getSubItemValueClass
'
,
()
=>
{
it
(
'
returns string containing `label-dark` when provided subItem is greater than current week day
'
,
()
=>
{
mountComponent
({
currentDate
:
new
Date
(
2018
,
0
,
1
),
// Jan 1, 2018
});
const
subItem
=
new
Date
(
2018
,
0
,
25
);
// Jan 25, 2018
expect
(
wrapper
.
vm
.
getSubItemValueClass
(
subItem
)).
toBe
(
'
label-dark
'
);
});
it
(
'
returns string containing `label-dark label-bold` when provided subItem is same as current week day
'
,
()
=>
{
const
currentDate
=
new
Date
(
2018
,
0
,
25
);
mountComponent
({
currentDate
,
});
const
subItem
=
currentDate
;
expect
(
wrapper
.
vm
.
getSubItemValueClass
(
subItem
)).
toBe
(
'
label-dark label-bold
'
);
});
});
});
describe
(
'
template
'
,
()
=>
{
it
(
'
renders component container element with class `item-sublabel`
'
,
()
=>
{
expect
(
wrapper
.
classes
()).
toContain
(
'
item-sublabel
'
);
...
...
@@ -83,7 +53,29 @@ describe('MonthsHeaderSubItemComponent', () => {
});
it
(
'
renders element with class `current-day-indicator-header` when hasToday is true
'
,
()
=>
{
// January 1st, 2018 is the first day of the week-long timeframe
// so as long as current date (faked January 3rd, 2018) is within week timeframe
// current indicator will be rendered
expect
(
wrapper
.
find
(
'
.current-day-indicator-header.preset-weeks
'
).
exists
()).
toBe
(
true
);
});
it
(
'
sublabel has `label-dark` class when it is for the day greater than current week day
'
,
()
=>
{
// Timeframe starts at Jan 1, 2018, faked today is Jan 3, 2018 (3rd item in a week timeframe)
// labels for dates after current have 'label-dark' class
expect
(
findSublabelValues
()
.
at
(
3
)
.
classes
(),
).
toContain
(
'
label-dark
'
);
});
it
(
"
sublabel has `label-dark label-bold` classes when it is for today's date
"
,
()
=>
{
// Timeframe starts at Jan 1, 2018, faked today is Jan 3, 2018 (3rd item in a week timeframe)
expect
(
findSublabelValues
()
.
at
(
2
)
.
classes
(),
).
toEqual
(
expect
.
arrayContaining
([
'
label-dark
'
,
'
label-bold
'
]));
});
});
});
ee/spec/frontend/oncall_schedule/schedule/components/rotations_list_section_spec.js
0 → 100644
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlCard
}
from
'
@gitlab/ui
'
;
import
RotationsListSection
from
'
ee/oncall_schedules/components/schedule/components/rotations_list_section.vue
'
;
import
CurrentDayIndicator
from
'
ee/oncall_schedules/components/schedule/components/current_day_indicator.vue
'
;
import
{
getTimeframeForWeeksView
}
from
'
ee/oncall_schedules/components/schedule/utils
'
;
import
{
PRESET_TYPES
}
from
'
ee/oncall_schedules/components/schedule/constants
'
;
describe
(
'
RotationsListSectionComponent
'
,
()
=>
{
let
wrapper
;
const
mockTimeframeInitialDate
=
new
Date
(
2018
,
0
,
1
);
const
mockTimeframeWeeks
=
getTimeframeForWeeksView
(
mockTimeframeInitialDate
);
function
mountComponent
({
presetType
=
PRESET_TYPES
.
WEEKS
,
timeframe
=
mockTimeframeWeeks
,
}
=
{})
{
wrapper
=
shallowMount
(
RotationsListSection
,
{
propsData
:
{
presetType
,
timeframe
,
rotations
:
[],
},
stubs
:
{
GlCard
,
},
});
}
beforeEach
(()
=>
{
mountComponent
();
});
afterEach
(()
=>
{
if
(
wrapper
)
{
wrapper
.
destroy
();
wrapper
=
null
;
}
});
const
findTimelineCells
=
()
=>
wrapper
.
findAll
(
'
[data-testid="timelineCell"]
'
);
it
(
'
renders component layout
'
,
()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
it
(
'
renders timeline cell items based on timeframe data
'
,
()
=>
{
expect
(
findTimelineCells
().
length
).
toBe
(
mockTimeframeWeeks
.
length
);
});
it
(
'
renders current day indicator in the first timeline cell
'
,
()
=>
{
expect
(
findTimelineCells
()
.
at
(
0
)
.
find
(
CurrentDayIndicator
)
.
exists
(),
).
toBe
(
true
);
});
});
ee/spec/frontend/oncall_schedule/schedule/components/schedule_timeline_section_spec.js
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlCard
,
GlButton
}
from
'
@gitlab/ui
'
;
import
ScheduleTimelineSection
,
{
i18n
,
}
from
'
ee/oncall_schedules/components/schedule/components/schedule_timeline_section.vue
'
;
import
{
GlCard
}
from
'
@gitlab/ui
'
;
import
ScheduleTimelineSection
from
'
ee/oncall_schedules/components/schedule/components/schedule_timeline_section.vue
'
;
import
WeeksHeaderItem
from
'
ee/oncall_schedules/components/schedule/components/preset_weeks/weeks_header_item.vue
'
;
import
{
getTimeframeForWeeksView
}
from
'
ee/oncall_schedules/components/schedule/utils
'
;
import
{
PRESET_TYPES
}
from
'
ee/oncall_schedules/components/schedule/constants
'
;
describe
(
'
Roadmap
TimelineSectionComponent
'
,
()
=>
{
describe
(
'
TimelineSectionComponent
'
,
()
=>
{
let
wrapper
;
const
mockTimeframeInitialDate
=
new
Date
(
2018
,
0
,
1
);
const
mockTimeframeWeeks
=
getTimeframeForWeeksView
(
mockTimeframeInitialDate
);
const
findRotations
=
()
=>
wrapper
.
find
(
GlCard
);
const
findAddRotation
=
()
=>
wrapper
.
find
(
GlButton
);
function
mountComponent
({
presetType
=
PRESET_TYPES
.
WEEKS
,
timeframe
=
mockTimeframeWeeks
,
...
...
@@ -52,13 +47,4 @@ describe('RoadmapTimelineSectionComponent', () => {
it
(
'
renders weeks header items based on timeframe data
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
WeeksHeaderItem
).
length
).
toBe
(
mockTimeframeWeeks
.
length
);
});
it
(
'
renders the rotation card wrapper
'
,
()
=>
{
expect
(
findRotations
().
exists
()).
toBe
(
true
);
});
it
(
'
renders the add rotation button in the rotation card wrapper
'
,
()
=>
{
expect
(
findAddRotation
().
exists
()).
toBe
(
true
);
expect
(
findAddRotation
().
text
()).
toBe
(
i18n
.
addARotation
);
});
});
ee/spec/frontend/oncall_schedule/schedule/mixins/common_mixin_spec.js
0 → 100644
View file @
f908ee29
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
CommonMixin
from
'
ee/oncall_schedules/components/schedule/mixins/common_mixin
'
;
import
{
DAYS_IN_WEEK
}
from
'
ee/oncall_schedules/components/schedule/constants
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
describe
(
'
Schedule Common Mixins
'
,
()
=>
{
// January 3rd, 2018
useFakeDate
(
2018
,
0
,
3
);
const
today
=
new
Date
();
let
wrapper
;
const
component
=
{
template
:
`<span></span>`
,
props
:
{
timeframeItem
:
{
type
:
[
Date
,
Object
],
required
:
true
,
},
},
mixins
:
[
CommonMixin
],
};
const
mountComponent
=
(
props
=
{})
=>
{
wrapper
=
shallowMount
(
component
,
{
propsData
:
{
timeframeItem
:
today
,
...
props
,
},
});
};
describe
(
'
data
'
,
()
=>
{
it
(
'
initializes currentDate default value
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
vm
.
$options
.
currentDate
).
toEqual
(
today
);
});
});
describe
(
'
hasToday
'
,
()
=>
{
it
(
'
returns true when today (January 3rd, 2018) is within the set week (January 1st, 2018)
'
,
()
=>
{
// January 1st, 2018
mountComponent
({
timeframeItem
:
new
Date
(
2018
,
0
,
1
),
});
expect
(
wrapper
.
vm
.
hasToday
).
toBe
(
true
);
});
it
(
'
returns false when today (January 3rd, 2018) is NOT within the set week (January 8th, 2018)
'
,
()
=>
{
// February 1st, 2018
mountComponent
({
timeframeItem
:
new
Date
(
2018
,
0
,
8
),
});
expect
(
wrapper
.
vm
.
hasToday
).
toBe
(
false
);
});
});
describe
(
'
getIndicatorStyles
'
,
()
=>
{
it
(
'
returns object containing `left` offset
'
,
()
=>
{
const
left
=
100
/
DAYS_IN_WEEK
/
2
;
expect
(
wrapper
.
vm
.
getIndicatorStyles
()).
toEqual
(
expect
.
objectContaining
({
left
:
`
${
left
}
%`
,
}),
);
});
});
});
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