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
116cd1d7
Commit
116cd1d7
authored
Dec 03, 2020
by
Natalia Tepluhina
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '262857-add-a-rotation-modal' into 'master'
Add rotation modal See merge request gitlab-org/gitlab!48553
parents
36ac4071
3eb079ea
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1090 additions
and
0 deletions
+1090
-0
app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
...scripts/graphql_shared/queries/users_search.query.graphql
+9
-0
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
.../oncall_schedules/components/oncall_schedules_wrapper.vue
+6
-0
ee/app/assets/javascripts/oncall_schedules/components/rotations/add_rotation_modal.vue
...all_schedules/components/rotations/add_rotation_modal.vue
+280
-0
ee/app/assets/javascripts/oncall_schedules/constants.js
ee/app/assets/javascripts/oncall_schedules/constants.js
+9
-0
ee/app/assets/javascripts/oncall_schedules/graphql/create_oncall_schedule_rotation.mutation.graphql
.../graphql/create_oncall_schedule_rotation.mutation.graphql
+26
-0
ee/spec/frontend/oncall_schedule/mocks/apollo_mock.js
ee/spec/frontend/oncall_schedule/mocks/apollo_mock.js
+16
-0
ee/spec/frontend/oncall_schedule/rotations/__snapshots__/add_rotation_modal_spec.js.snap
...e/rotations/__snapshots__/add_rotation_modal_spec.js.snap
+518
-0
ee/spec/frontend/oncall_schedule/rotations/add_rotation_modal_spec.js
...tend/oncall_schedule/rotations/add_rotation_modal_spec.js
+196
-0
locale/gitlab.pot
locale/gitlab.pot
+30
-0
No files found.
app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
0 → 100644
View file @
116cd1d7
#import "../fragments/user.fragment.graphql"
query
usersSearch
(
$search
:
String
!)
{
users
(
search
:
$search
)
{
nodes
{
...
User
}
}
}
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
View file @
116cd1d7
<
script
>
import
{
GlEmptyState
,
GlButton
,
GlModalDirective
}
from
'
@gitlab/ui
'
;
import
AddScheduleModal
from
'
./add_schedule_modal.vue
'
;
import
AddRotationModal
from
'
./rotations/add_rotation_modal.vue
'
;
import
{
s__
}
from
'
~/locale
'
;
const
addScheduleModalId
=
'
addScheduleModal
'
;
...
...
@@ -21,6 +22,7 @@ export default {
GlEmptyState
,
GlButton
,
AddScheduleModal
,
AddRotationModal
,
},
directives
:
{
GlModal
:
GlModalDirective
,
...
...
@@ -40,8 +42,12 @@ export default {
<gl-button
v-gl-modal=
"$options.addScheduleModalId"
variant=
"info"
>
{{
$options
.
i18n
.
emptyState
.
button
}}
</gl-button>
<gl-button
v-gl-modal=
"'create-schedule-rotation-modal'"
variant=
"danger"
>
{{
$options
.
i18n
.
emptyState
.
button
}}
</gl-button>
</
template
>
</gl-empty-state>
<add-schedule-modal
:modal-id=
"$options.addScheduleModalId"
/>
<add-rotation-modal
/>
</div>
</template>
ee/app/assets/javascripts/oncall_schedules/components/rotations/add_rotation_modal.vue
0 → 100644
View file @
116cd1d7
<
script
>
import
{
GlModal
,
GlForm
,
GlFormGroup
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlDatepicker
,
GlTokenSelector
,
GlAvatar
,
GlAvatarLabeled
,
GlAlert
,
}
from
'
@gitlab/ui
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
usersSearchQuery
from
'
~/graphql_shared/queries/users_search.query.graphql
'
;
import
createOncallScheduleRotationMutation
from
'
../../graphql/create_oncall_schedule_rotation.mutation.graphql
'
;
import
{
LENGTH_ENUM
,
CHEVRON_SKIPPING_SHADE_ENUM
,
CHEVRON_SKIPPING_PALETTE_ENUM
,
}
from
'
../../constants
'
;
export
default
{
i18n
:
{
selectParticipant
:
s__
(
'
OnCallSchedules|Select participant
'
),
addRotation
:
s__
(
'
OnCallSchedules|Add rotation
'
),
noResults
:
__
(
'
No matching results
'
),
cancel
:
__
(
'
Cancel
'
),
errorMsg
:
s__
(
'
OnCallSchedules|Failed to add rotation
'
),
fields
:
{
name
:
{
title
:
__
(
'
Name
'
),
error
:
s__
(
'
OnCallSchedules|Rotation name cannot be empty
'
)
},
participants
:
{
title
:
__
(
'
Participants
'
),
error
:
s__
(
'
OnCallSchedules|Rotation participants cannot be empty
'
),
},
length
:
{
title
:
s__
(
'
OnCallSchedules|Rotation length
'
)
},
startsOn
:
{
title
:
__
(
'
Starts on
'
),
error
:
s__
(
'
OnCallSchedules|Rotation start date cannot be empty
'
),
},
},
},
tokenColorPalette
:
{
shade
:
CHEVRON_SKIPPING_SHADE_ENUM
,
palette
:
CHEVRON_SKIPPING_PALETTE_ENUM
,
},
LENGTH_ENUM
,
inject
:
[
'
projectPath
'
],
components
:
{
GlModal
,
GlForm
,
GlFormGroup
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlDatepicker
,
GlTokenSelector
,
GlAvatar
,
GlAvatarLabeled
,
GlAlert
,
},
apollo
:
{
participants
:
{
query
:
usersSearchQuery
,
variables
()
{
return
{
search
:
this
.
ptSearchTerm
,
};
},
update
({
users
:
{
nodes
=
[]
}
=
{}
})
{
return
nodes
;
},
error
(
error
)
{
this
.
showErrorAlert
=
true
;
this
.
error
=
error
;
},
},
},
data
()
{
return
{
participants
:
[],
loading
:
false
,
ptSearchTerm
:
''
,
form
:
{
name
:
''
,
participants
:
[],
length
:
{
value
:
1
,
type
:
this
.
$options
.
LENGTH_ENUM
.
hours
,
},
startsOn
:
{
date
:
null
,
time
:
0
,
},
},
showErrorAlert
:
false
,
error
:
''
,
};
},
computed
:
{
actionsProps
()
{
return
{
primary
:
{
text
:
this
.
$options
.
i18n
.
addRotation
,
attributes
:
[{
variant
:
'
info
'
},
{
loading
:
this
.
loading
}],
},
cancel
:
{
text
:
this
.
$options
.
i18n
.
cancel
,
},
};
},
rotationNameIsValid
()
{
return
this
.
form
.
name
!==
''
;
},
rotationParticipantsAreValid
()
{
return
this
.
form
.
participants
.
length
>
0
;
},
rotationStartsOnIsValid
()
{
return
this
.
form
.
startsOn
.
date
!==
null
||
this
.
form
.
startsOn
.
date
!==
undefined
;
},
noResults
()
{
return
this
.
participants
.
length
===
0
;
},
},
methods
:
{
createRotation
()
{
this
.
loading
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
createOncallScheduleRotationMutation
,
variables
:
{
oncallScheduleRotationCreate
:
{
projectPath
:
this
.
projectPath
,
...
this
.
form
,
},
},
})
.
then
(({
data
:
{
oncallScheduleRotationCreate
:
{
errors
:
[
error
]
}
}
})
=>
{
if
(
error
)
{
throw
error
;
}
this
.
$refs
.
createScheduleModal
.
hide
();
})
.
catch
(
error
=>
{
this
.
error
=
error
;
this
.
showErrorAlert
=
true
;
})
.
finally
(()
=>
{
this
.
loading
=
false
;
});
},
formatTime
(
time
)
{
return
time
>
9
?
`
${
time
}
:00`
:
`0
${
time
}
:00`
;
},
filterParticipants
(
query
)
{
this
.
ptSearchTerm
=
query
;
},
setRotationLengthType
(
type
)
{
this
.
form
.
length
.
type
=
type
;
},
setRotationStartsOnTime
(
time
)
{
this
.
form
.
startsOn
.
time
=
time
;
},
},
};
</
script
>
<
template
>
<gl-modal
ref=
"createScheduleRotationModal"
modal-id=
"create-schedule-rotation-modal"
size=
"sm"
:title=
"$options.i18n.addRotation"
:action-primary=
"actionsProps.primary"
:action-cancel=
"actionsProps.cancel"
@
primary=
"createRotation"
>
<gl-alert
v-if=
"showErrorAlert"
variant=
"danger"
@
dismiss=
"showErrorAlert = false"
>
{{
error
||
$options
.
i18n
.
errorMsg
}}
</gl-alert>
<gl-form
class=
"w-75 gl-xs-w-full!"
@
submit.prevent=
"createRotation"
>
<gl-form-group
:label=
"$options.i18n.fields.name.title"
label-size=
"sm"
label-for=
"rotation-name"
:invalid-feedback=
"$options.i18n.fields.name.error"
:state=
"rotationNameIsValid"
>
<gl-form-input
id=
"rotation-name"
v-model=
"form.name"
/>
</gl-form-group>
<gl-form-group
:label=
"$options.i18n.fields.participants.title"
label-size=
"sm"
label-for=
"rotation-participants"
:invalid-feedback=
"$options.i18n.fields.participants.error"
:state=
"rotationParticipantsAreValid"
>
<gl-token-selector
v-model=
"form.participants"
:dropdown-items=
"participants"
:loading=
"this.$apollo.queries.participants.loading"
:container-class=
"'gl-h-13! gl-overflow-y-auto'"
@
text-input=
"filterParticipants"
>
<template
#token-content
="
{ token }">
<gl-avatar
v-if=
"token.avatarUrl"
:src=
"token.avatarUrl"
:size=
"16"
/>
{{
token
.
name
}}
</
template
>
<
template
#dropdown-item-content=
"{ dropdownItem }"
>
<gl-avatar-labeled
:src=
"dropdownItem.avatarUrl"
:size=
"32"
:label=
"dropdownItem.name"
:sub-label=
"dropdownItem.username"
/>
</
template
>
</gl-token-selector>
</gl-form-group>
<gl-form-group
:label=
"$options.i18n.fields.length.title"
label-size=
"sm"
label-for=
"rotation-length"
>
<div
class=
"gl-display-flex"
>
<gl-form-input
id=
"rotation-length"
v-model=
"form.length.value"
type=
"number"
class=
"gl-w-12 gl-mr-3"
min=
"1"
/>
<gl-dropdown
id=
"rotation-length"
:text=
"form.length.type"
>
<gl-dropdown-item
v-for=
"type in $options.LENGTH_ENUM"
:key=
"type"
:is-checked=
"form.length.type === type"
is-check-item
@
click=
"setRotationLengthType(type)"
>
{{ type }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</gl-form-group>
<gl-form-group
:label=
"$options.i18n.fields.startsOn.title"
label-size=
"sm"
label-for=
"rotation-time"
:invalid-feedback=
"$options.i18n.fields.startsOn.error"
:state=
"rotationStartsOnIsValid"
>
<div
class=
"gl-display-flex gl-align-items-center"
>
<gl-datepicker
v-model=
"form.startsOn.date"
class=
"gl-mr-3"
/>
<span>
{{ __('at') }}
</span>
<gl-dropdown
id=
"rotation-time"
:text=
"formatTime(form.startsOn.time)"
class=
"gl-w-12 gl-pl-3"
>
<gl-dropdown-item
v-for=
"n in 24"
:key=
"n"
:is-checked=
"form.startsOn.time === n"
is-check-item
@
click=
"setRotationStartsOnTime(n)"
>
<span
class=
"gl-white-space-nowrap"
>
{{ formatTime(n) }}
</span>
</gl-dropdown-item>
</gl-dropdown>
<!-- TODO: // Replace with actual timezone following coming work -->
<span
class=
"gl-pl-5"
>
{{ __('PST') }}
</span>
</div>
</gl-form-group>
</gl-form>
</gl-modal>
</template>
ee/app/assets/javascripts/oncall_schedules/constants.js
0 → 100644
View file @
116cd1d7
export
const
LENGTH_ENUM
=
{
hours
:
'
hours
'
,
days
:
'
days
'
,
weeks
:
'
weeks
'
,
};
export
const
CHEVRON_SKIPPING_SHADE_ENUM
=
[
'
500
'
,
'
600
'
,
'
700
'
,
'
800
'
,
'
900
'
,
'
950
'
];
export
const
CHEVRON_SKIPPING_PALETTE_ENUM
=
[
'
blue
'
,
'
orange
'
,
'
aqua
'
,
'
green
'
,
'
magenta
'
];
ee/app/assets/javascripts/oncall_schedules/graphql/create_oncall_schedule_rotation.mutation.graphql
0 → 100644
View file @
116cd1d7
#import "~/graphql_shared/fragments/user.fragment.graphql"
mutation
oncallScheduleRotationCreate
(
$oncallScheduleRotationCreateInput
:
OncallScheduleRotationCreateInput
!
)
{
oncallScheduleRotationCreate
(
input
:
$oncallScheduleRotationCreateInput
)
{
errors
oncallScheduleRotation
{
iid
name
participants
{
nodes
{
...
User
}
}
length
{
value
type
}
startsOn
{
date
time
}
}
}
}
ee/spec/frontend/oncall_schedule/mocks/apollo_mock.js
0 → 100644
View file @
116cd1d7
export
const
participants
=
[
{
id
:
'
1
'
,
username
:
'
test
'
,
name
:
'
test
'
,
avatar
:
''
,
avatarUrl
:
''
,
},
{
id
:
'
2
'
,
username
:
'
hello
'
,
name
:
'
hello
'
,
avatar
:
''
,
avatarUrl
:
''
,
},
];
ee/spec/frontend/oncall_schedule/rotations/__snapshots__/add_rotation_modal_spec.js.snap
0 → 100644
View file @
116cd1d7
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AddRotationModal renders rotation modal layout 1`] = `
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
modalclass=""
modalid="create-schedule-rotation-modal"
size="sm"
title="Add rotation"
titletag="h4"
>
<!---->
<gl-form-stub
class="w-75 gl-xs-w-full!"
>
<gl-form-group-stub
invalid-feedback="Rotation name cannot be empty"
label="Name"
label-for="rotation-name"
label-size="sm"
>
<gl-form-input-stub
id="rotation-name"
value=""
/>
</gl-form-group-stub>
<gl-form-group-stub
invalid-feedback="Rotation participants cannot be empty"
label="Participants"
label-for="rotation-participants"
label-size="sm"
>
<gl-token-selector-stub
autocomplete="off"
containerclass="gl-h-13! gl-overflow-y-auto"
dropdownitems=""
selectedtokens=""
/>
</gl-form-group-stub>
<gl-form-group-stub
label="Rotation length"
label-for="rotation-length"
label-size="sm"
>
<div
class="gl-display-flex"
>
<gl-form-input-stub
class="gl-w-12 gl-mr-3"
id="rotation-length"
min="1"
type="number"
value="1"
/>
<gl-dropdown-stub
category="primary"
headertext=""
id="rotation-length"
size="medium"
text="hours"
variant="default"
>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischecked="true"
ischeckitem="true"
secondarytext=""
>
hours
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
days
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
weeks
</gl-dropdown-item-stub>
</gl-dropdown-stub>
</div>
</gl-form-group-stub>
<gl-form-group-stub
invalid-feedback="Rotation start date cannot be empty"
label="Starts on"
label-for="rotation-time"
label-size="sm"
state="true"
>
<div
class="gl-display-flex gl-align-items-center"
>
<gl-datepicker-stub
ariallabel=""
autocomplete=""
class="gl-mr-3"
container=""
displayfield="true"
firstday="0"
placeholder="YYYY-MM-DD"
target=""
theme=""
/>
<span>
at
</span>
<gl-dropdown-stub
category="primary"
class="gl-w-12 gl-pl-3"
headertext=""
id="rotation-time"
size="medium"
text="00:00"
variant="default"
>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
01:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
02:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
03:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
04:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
05:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
06:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
07:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
08:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
09:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
10:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
11:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
12:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
13:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
14:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
15:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
16:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
17:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
18:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
19:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
20:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
21:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
22:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
23:00
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
24:00
</span>
</gl-dropdown-item-stub>
</gl-dropdown-stub>
<span
class="gl-pl-5"
>
PST
</span>
</div>
</gl-form-group-stub>
</gl-form-stub>
</gl-modal-stub>
`;
ee/spec/frontend/oncall_schedule/rotations/add_rotation_modal_spec.js
0 → 100644
View file @
116cd1d7
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
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
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
'
;
import
{
participants
}
from
'
../mocks/apollo_mock
'
;
const
localVue
=
createLocalVue
();
const
projectPath
=
'
group/project
'
;
const
mutate
=
jest
.
fn
();
const
mockHideModal
=
jest
.
fn
();
localVue
.
use
(
VueApollo
);
describe
(
'
AddRotationModal
'
,
()
=>
{
let
wrapper
;
let
fakeApollo
;
let
userSearchQueryHandler
;
async
function
awaitApolloDomMock
()
{
await
wrapper
.
vm
.
$nextTick
();
// kick off the DOM update
await
jest
.
runOnlyPendingTimers
();
// kick off the mocked GQL stuff (promises)
await
wrapper
.
vm
.
$nextTick
();
// kick off the DOM update for flash
}
const
createComponent
=
({
data
=
{},
props
=
{},
loading
=
false
}
=
{})
=>
{
wrapper
=
shallowMount
(
AddRotationModal
,
{
data
()
{
return
{
...
data
,
};
},
propsData
:
{
...
props
,
},
provide
:
{
projectPath
,
},
mocks
:
{
$apollo
:
{
queries
:
{
participants
:
{
loading
,
},
},
mutate
,
},
},
});
wrapper
.
vm
.
$refs
.
createScheduleRotationModal
.
hide
=
mockHideModal
;
};
const
createComponentWithApollo
=
({
search
=
''
}
=
{})
=>
{
fakeApollo
=
createMockApollo
([[
usersSearchQuery
,
userSearchQueryHandler
]]);
wrapper
=
shallowMount
(
AddRotationModal
,
{
localVue
,
apolloProvider
:
fakeApollo
,
data
()
{
return
{
ptSearchTerm
:
search
,
form
:
{
participants
,
},
participants
,
};
},
provide
:
{
projectPath
,
},
});
};
beforeEach
(()
=>
{
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
const
findModal
=
()
=>
wrapper
.
find
(
GlModal
);
const
findRotationLength
=
()
=>
wrapper
.
find
(
'
[id = "rotation-length"]
'
);
const
findRotationStartsOn
=
()
=>
wrapper
.
find
(
'
[id = "rotation-time"]
'
);
const
findUserSelector
=
()
=>
wrapper
.
find
(
GlTokenSelector
);
const
findDropdownOptions
=
()
=>
wrapper
.
findAll
(
GlDropdownItem
);
const
findAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
it
(
'
renders rotation modal layout
'
,
()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
describe
(
'
Rotation length and start time
'
,
()
=>
{
it
(
'
renders the rotation length value
'
,
async
()
=>
{
const
rotationLength
=
findRotationLength
();
expect
(
rotationLength
.
exists
()).
toBe
(
true
);
expect
(
rotationLength
.
attributes
(
'
value
'
)).
toBe
(
'
1
'
);
});
it
(
'
renders the rotation starts on datepicker
'
,
async
()
=>
{
const
startsOn
=
findRotationStartsOn
();
expect
(
startsOn
.
exists
()).
toBe
(
true
);
expect
(
startsOn
.
attributes
(
'
text
'
)).
toBe
(
'
00:00
'
);
expect
(
startsOn
.
attributes
(
'
headertext
'
)).
toBe
(
''
);
});
it
(
'
should add a check for a rotation length type selected
'
,
async
()
=>
{
const
selectedLengthType1
=
findDropdownOptions
().
at
(
0
);
const
selectedLengthType2
=
findDropdownOptions
().
at
(
1
);
selectedLengthType1
.
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
selectedLengthType1
.
props
(
'
isChecked
'
)).
toBe
(
true
);
expect
(
selectedLengthType2
.
props
(
'
isChecked
'
)).
toBe
(
false
);
});
});
describe
(
'
filter participants
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
({
data
:
{
participants
}
});
});
it
(
'
has user options that are populated via apollo
'
,
()
=>
{
expect
(
findUserSelector
().
props
(
'
dropdownItems
'
).
length
).
toBe
(
participants
.
length
);
});
it
(
'
calls the API and sets dropdown items as request result
'
,
async
()
=>
{
const
tokenSelector
=
findUserSelector
();
tokenSelector
.
vm
.
$emit
(
'
focus
'
);
tokenSelector
.
vm
.
$emit
(
'
blur
'
);
tokenSelector
.
vm
.
$emit
(
'
focus
'
);
await
waitForPromises
();
expect
(
tokenSelector
.
props
(
'
dropdownItems
'
)).
toMatchObject
(
participants
);
expect
(
tokenSelector
.
props
(
'
hideDropdownWithNoItems
'
)).
toBe
(
false
);
});
it
(
'
emits `input` event with selected users
'
,
()
=>
{
findUserSelector
().
vm
.
$emit
(
'
input
'
,
participants
);
expect
(
findUserSelector
().
emitted
().
input
[
0
][
0
]).
toEqual
(
participants
);
});
it
(
'
when text input is blurred the text input clears
'
,
async
()
=>
{
const
tokenSelector
=
findUserSelector
();
tokenSelector
.
vm
.
$emit
(
'
blur
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
tokenSelector
.
props
(
'
hideDropdownWithNoItems
'
)).
toBe
(
false
);
});
});
describe
(
'
Rotation create
'
,
()
=>
{
it
(
'
makes a request with `oncallScheduleRotationCreate` to create a schedule rotation
'
,
()
=>
{
mutate
.
mockResolvedValueOnce
({});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
expect
(
mutate
).
toHaveBeenCalledWith
({
mutation
:
expect
.
any
(
Object
),
variables
:
{
oncallScheduleRotationCreate
:
expect
.
objectContaining
({
projectPath
})
},
});
});
it
(
'
does not hide the rotation modal and shows error alert on fail
'
,
async
()
=>
{
const
error
=
'
some error
'
;
mutate
.
mockResolvedValueOnce
({
data
:
{
oncallScheduleRotationCreate
:
{
errors
:
[
error
]
}
}
});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
await
waitForPromises
();
expect
(
mockHideModal
).
not
.
toHaveBeenCalled
();
expect
(
findAlert
().
exists
()).
toBe
(
true
);
expect
(
findAlert
().
text
()).
toContain
(
error
);
});
});
describe
(
'
with mocked Apollo client
'
,
()
=>
{
it
(
'
it calls searchUsers query with the search paramter
'
,
async
()
=>
{
userSearchQueryHandler
=
jest
.
fn
().
mockResolvedValue
({
data
:
{
users
:
{
nodes
:
participants
,
},
},
});
createComponentWithApollo
({
search
:
'
root
'
});
await
awaitApolloDomMock
();
expect
(
userSearchQueryHandler
).
toHaveBeenCalledWith
({
search
:
'
root
'
});
});
// TODO: Once the BE is complete for the mutation add specs here for that via a creationHandler
});
});
locale/gitlab.pot
View file @
116cd1d7
...
...
@@ -19085,18 +19085,39 @@ msgstr ""
msgid "OnCallSchedules|Add a schedule"
msgstr ""
msgid "OnCallSchedules|Add rotation"
msgstr ""
msgid "OnCallSchedules|Add schedule"
msgstr ""
msgid "OnCallSchedules|Create on-call schedules in GitLab"
msgstr ""
msgid "OnCallSchedules|Failed to add rotation"
msgstr ""
msgid "OnCallSchedules|Failed to add schedule"
msgstr ""
msgid "OnCallSchedules|Rotation length"
msgstr ""
msgid "OnCallSchedules|Rotation name cannot be empty"
msgstr ""
msgid "OnCallSchedules|Rotation participants cannot be empty"
msgstr ""
msgid "OnCallSchedules|Rotation start date cannot be empty"
msgstr ""
msgid "OnCallSchedules|Route alerts directly to specific members of your team"
msgstr ""
msgid "OnCallSchedules|Select participant"
msgstr ""
msgid "OnCallSchedules|Select timezone"
msgstr ""
...
...
@@ -19414,6 +19435,9 @@ msgstr ""
msgid "Owner"
msgstr ""
msgid "PST"
msgstr ""
msgid "Package Registry"
msgstr ""
...
...
@@ -26042,6 +26066,9 @@ msgstr ""
msgid "Starts at (UTC)"
msgstr ""
msgid "Starts on"
msgstr ""
msgid "State your message to activate"
msgstr ""
...
...
@@ -31920,6 +31947,9 @@ msgstr ""
msgid "assign yourself"
msgstr ""
msgid "at"
msgstr ""
msgid "at risk"
msgstr ""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment