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
01673739
Commit
01673739
authored
May 31, 2021
by
Olena Horal-Koretska
Committed by
Andrew Fontaine
May 31, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add escalation policy BE integration
parent
26a50a0c
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
476 additions
and
94 deletions
+476
-94
app/assets/stylesheets/page_bundles/escalation_policies.scss
app/assets/stylesheets/page_bundles/escalation_policies.scss
+8
-7
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_form.vue
...n_policies/components/add_edit_escalation_policy_form.vue
+55
-9
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_modal.vue
..._policies/components/add_edit_escalation_policy_modal.vue
+72
-4
ee/app/assets/javascripts/escalation_policies/components/escalation_rule.vue
...cripts/escalation_policies/components/escalation_rule.vue
+140
-54
ee/app/assets/javascripts/escalation_policies/constants.js
ee/app/assets/javascripts/escalation_policies/constants.js
+1
-4
ee/app/assets/javascripts/escalation_policies/graphql/mutations/create_escalation_policy.mutation.graphql
...aphql/mutations/create_escalation_policy.mutation.graphql
+18
-0
ee/app/assets/javascripts/escalation_policies/graphql/queries/get_oncall_schedules.query.graphql
...licies/graphql/queries/get_oncall_schedules.query.graphql
+10
-0
ee/app/assets/javascripts/escalation_policies/index.js
ee/app/assets/javascripts/escalation_policies/index.js
+23
-0
ee/app/assets/javascripts/escalation_policies/utils.js
ee/app/assets/javascripts/escalation_policies/utils.js
+10
-0
ee/app/helpers/incident_management/escalation_policy_helper.rb
...p/helpers/incident_management/escalation_policy_helper.rb
+3
-2
ee/app/views/projects/incident_management/escalation_policies/index.html.haml
...s/incident_management/escalation_policies/index.html.haml
+1
-1
ee/lib/ee/sidebars/projects/menus/monitor_menu.rb
ee/lib/ee/sidebars/projects/menus/monitor_menu.rb
+1
-1
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_form_spec.js
...calation_policies/add_edit_escalation_policy_form_spec.js
+40
-5
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_modal_spec.js
...alation_policies/add_edit_escalation_policy_modal_spec.js
+57
-1
ee/spec/frontend/escalation_policies/escalation_rule_spec.js
ee/spec/frontend/escalation_policies/escalation_rule_spec.js
+20
-1
ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb
...pers/incident_management/escalation_policy_helper_spec.rb
+4
-1
ee/spec/lib/ee/sidebars/projects/menus/monitor_menu_spec.rb
ee/spec/lib/ee/sidebars/projects/menus/monitor_menu_spec.rb
+1
-1
ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+3
-3
locale/gitlab.pot
locale/gitlab.pot
+9
-0
No files found.
app/assets/stylesheets/page_bundles/escalation_policies.scss
View file @
01673739
...
...
@@ -2,13 +2,14 @@
width
:
640px
;
}
.escalation-policy-rules
{
.rule-control
{
width
:
240px
;
}
.rule-control
{
width
:
240px
;
}
.rule-elapsed-minutes
{
width
:
56px
;
}
.rule-elapsed-minutes
{
width
:
56px
;
}
.rule-close-icon
{
right
:
1rem
;
}
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_form.vue
View file @
01673739
<
script
>
import
{
GlLink
,
GlForm
,
GlFormGroup
,
GlFormInput
}
from
'
@gitlab/ui
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
createFlash
from
'
~/flash
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
defaultEscalationRule
}
from
'
../constants
'
;
import
getOncallSchedulesQuery
from
'
../graphql/queries/get_oncall_schedules.query.graphql
'
;
import
EscalationRule
from
'
./escalation_rule.vue
'
;
export
const
i18n
=
{
...
...
@@ -19,6 +21,7 @@ export const i18n = {
},
},
addRule
:
s__
(
'
EscalationPolicies|+ Add an additional rule
'
),
failedLoadingSchedules
:
s__
(
'
EscalationPolicies|Failed to load oncall-schedules
'
),
};
export
default
{
...
...
@@ -30,6 +33,7 @@ export default {
GlFormInput
,
EscalationRule
,
},
inject
:
[
'
projectPath
'
],
props
:
{
form
:
{
type
:
Object
,
...
...
@@ -42,12 +46,50 @@ export default {
},
data
()
{
return
{
rules
:
[
cloneDeep
(
defaultEscalationRule
)],
schedules
:
[],
rules
:
[],
uid
:
0
,
};
},
apollo
:
{
schedules
:
{
query
:
getOncallSchedulesQuery
,
variables
()
{
return
{
projectPath
:
this
.
projectPath
,
};
},
update
(
data
)
{
const
nodes
=
data
.
project
?.
incidentManagementOncallSchedules
?.
nodes
??
[];
return
nodes
;
},
error
(
error
)
{
createFlash
({
message
:
i18n
.
failedLoadingSchedules
,
captureError
:
true
,
error
});
},
},
},
mounted
()
{
this
.
rules
.
push
({
...
cloneDeep
(
defaultEscalationRule
),
key
:
this
.
getUid
()
});
},
methods
:
{
addRule
()
{
this
.
rules
.
push
(
cloneDeep
(
defaultEscalationRule
));
this
.
rules
.
push
({
...
cloneDeep
(
defaultEscalationRule
),
key
:
this
.
getUid
()
});
this
.
emitUpdate
();
},
updateEscalationRules
(
index
,
rule
)
{
this
.
rules
[
index
]
=
rule
;
this
.
emitUpdate
();
},
removeEscalationRule
(
index
)
{
this
.
rules
.
splice
(
index
,
1
);
this
.
emitUpdate
();
},
emitUpdate
()
{
this
.
$emit
(
'
update-escalation-policy-form
'
,
{
field
:
'
rules
'
,
value
:
this
.
rules
});
},
getUid
()
{
this
.
uid
+=
1
;
return
this
.
uid
;
},
},
};
...
...
@@ -92,13 +134,17 @@ export default {
</gl-form-group>
</div>
<gl-form-group
class=
"escalation-policy-rules"
:label=
"$options.i18n.fields.rules.title"
label-size=
"sm"
:state=
"validationState.rules"
>
<escalation-rule
v-for=
"(rule, index) in rules"
:key=
"index"
:rule=
"rule"
/>
<gl-form-group
class=
"gl-mb-3"
:label=
"$options.i18n.fields.rules.title"
label-size=
"sm"
>
<escalation-rule
v-for=
"(rule, index) in rules"
:key=
"rule.key"
:rule=
"rule"
:index=
"index"
:schedules=
"schedules"
:is-valid=
"validationState.rules[index]"
@
update-escalation-rule=
"updateEscalationRules"
@
remove-escalation-rule=
"removeEscalationRule"
/>
</gl-form-group>
<gl-link
@
click=
"addRule"
>
<span>
{{
$options
.
i18n
.
addRule
}}
</span>
...
...
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_modal.vue
View file @
01673739
<
script
>
import
{
GlModal
}
from
'
@gitlab/ui
'
;
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
set
}
from
'
lodash
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
addEscalationPolicyModalId
}
from
'
../constants
'
;
import
{
isNameFieldValid
}
from
'
../utils
'
;
import
createEscalationPolicyMutation
from
'
../graphql/mutations/create_escalation_policy.mutation.graphql
'
;
import
{
isNameFieldValid
,
getRulesValidationState
}
from
'
../utils
'
;
import
AddEditEscalationPolicyForm
from
'
./add_edit_escalation_policy_form.vue
'
;
export
const
i18n
=
{
...
...
@@ -17,8 +18,10 @@ export default {
addEscalationPolicyModalId
,
components
:
{
GlModal
,
GlAlert
,
AddEditEscalationPolicyForm
,
},
inject
:
[
'
projectPath
'
],
props
:
{
escalationPolicy
:
{
type
:
Object
,
...
...
@@ -32,11 +35,13 @@ export default {
form
:
{
name
:
this
.
escalationPolicy
.
name
,
description
:
this
.
escalationPolicy
.
description
,
rules
:
[],
},
validationState
:
{
name
:
true
,
rules
:
true
,
rules
:
[]
,
},
error
:
null
,
};
},
computed
:
{
...
...
@@ -56,7 +61,15 @@ export default {
};
},
isFormValid
()
{
return
Object
.
values
(
this
.
validationState
).
every
(
Boolean
);
return
this
.
validationState
.
name
&&
this
.
validationState
.
rules
.
every
(
Boolean
);
},
serializedData
()
{
const
rules
=
this
.
form
.
rules
.
map
(({
status
,
elapsedTimeSeconds
,
oncallScheduleIid
})
=>
({
status
,
elapsedTimeSeconds
,
oncallScheduleIid
,
}));
return
{
...
this
.
form
,
rules
};
},
},
methods
:
{
...
...
@@ -64,10 +77,59 @@ export default {
set
(
this
.
form
,
field
,
value
);
this
.
validateForm
(
field
);
},
createEscalationPolicy
()
{
this
.
loading
=
true
;
const
{
projectPath
}
=
this
;
this
.
$apollo
.
mutate
({
mutation
:
createEscalationPolicyMutation
,
variables
:
{
input
:
{
projectPath
,
...
this
.
serializedData
,
},
},
})
.
then
(
({
data
:
{
escalationPolicyCreate
:
{
errors
:
[
error
],
},
},
})
=>
{
if
(
error
)
{
throw
error
;
}
this
.
$refs
.
addUpdateEscalationPolicyModal
.
hide
();
this
.
$emit
(
'
policyCreated
'
);
this
.
clearForm
();
},
)
.
catch
((
error
)
=>
{
this
.
error
=
error
;
})
.
finally
(()
=>
{
this
.
loading
=
false
;
});
},
validateForm
(
field
)
{
if
(
field
===
'
name
'
)
{
this
.
validationState
.
name
=
isNameFieldValid
(
this
.
form
.
name
);
}
if
(
field
===
'
rules
'
)
{
this
.
validationState
.
rules
=
getRulesValidationState
(
this
.
form
.
rules
);
}
},
hideErrorAlert
()
{
this
.
error
=
null
;
},
clearForm
()
{
this
.
form
=
{
name
:
''
,
description
:
''
,
rules
:
[],
};
},
},
};
...
...
@@ -75,12 +137,18 @@ export default {
<
template
>
<gl-modal
ref=
"addUpdateEscalationPolicyModal"
class=
"escalation-policy-modal"
:modal-id=
"$options.addEscalationPolicyModalId"
:title=
"$options.i18n.addEscalationPolicy"
:action-primary=
"actionsProps.primary"
:action-cancel=
"actionsProps.cancel"
@
primary.prevent=
"createEscalationPolicy"
@
cancel=
"clearForm"
>
<gl-alert
v-if=
"error"
variant=
"danger"
class=
"gl-mt-n3 gl-mb-3"
@
dismiss=
"hideErrorAlert"
>
{{
error
}}
</gl-alert>
<add-edit-escalation-policy-form
:validation-state=
"validationState"
:form=
"form"
...
...
ee/app/assets/javascripts/escalation_policies/components/escalation_rule.vue
View file @
01673739
<
script
>
import
{
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlCard
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
GlFormGroup
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlCard
,
GlIcon
,
GlSprintf
,
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
ACTIONS
,
ALERT_STATUSES
}
from
'
../constants
'
;
...
...
@@ -9,6 +17,9 @@ export const i18n = {
condition
:
s__
(
'
EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes
'
),
action
:
s__
(
'
EscalationPolicies|THEN %{doAction} %{schedule}
'
),
selectSchedule
:
s__
(
'
EscalationPolicies|Select schedule
'
),
validationMsg
:
s__
(
'
EscalationPolicies|A schedule is required for adding an escalation policy.
'
,
),
},
},
};
...
...
@@ -18,10 +29,12 @@ export default {
ALERT_STATUSES
,
ACTIONS
,
components
:
{
GlFormGroup
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlCard
,
GlIcon
,
GlSprintf
,
},
props
:
{
...
...
@@ -34,65 +47,138 @@ export default {
required
:
false
,
default
:
()
=>
[],
},
index
:
{
type
:
Number
,
required
:
true
,
},
isValid
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
},
data
()
{
const
{
status
,
elapsedTimeSeconds
,
action
,
oncallScheduleIid
}
=
this
.
rule
;
return
{
status
,
elapsedTimeSeconds
,
action
,
oncallScheduleIid
,
};
},
computed
:
{
scheduleDropdownTitle
()
{
return
this
.
oncallScheduleIid
?
this
.
schedules
.
find
(({
iid
})
=>
iid
===
this
.
oncallScheduleIid
)?.
name
:
i18n
.
fields
.
rules
.
selectSchedule
;
},
},
methods
:
{
setOncallSchedule
({
iid
})
{
this
.
oncallScheduleIid
=
this
.
oncallScheduleIid
===
iid
?
null
:
iid
;
this
.
emitUpdate
();
},
setStatus
(
status
)
{
this
.
status
=
status
;
this
.
emitUpdate
();
},
emitUpdate
()
{
this
.
$emit
(
'
update-escalation-rule
'
,
this
.
index
,
{
oncallScheduleIid
:
parseInt
(
this
.
oncallScheduleIid
,
10
),
action
:
this
.
action
,
status
:
this
.
status
,
elapsedTimeSeconds
:
parseInt
(
this
.
elapsedTimeSeconds
,
10
),
});
},
},
};
</
script
>
<
template
>
<gl-card
class=
"gl-border-gray-400 gl-bg-gray-10 gl-mb-3"
>
<div
class=
"gl-display-flex gl-align-items-center"
>
<gl-sprintf
:message=
"$options.i18n.fields.rules.condition"
>
<template
#alertStatus
>
<gl-dropdown
class=
"rule-control gl-mx-3"
:text=
"$options.ALERT_STATUSES[rule.status]"
data-testid=
"alert-status-dropdown"
>
<gl-dropdown-item
v-for=
"(label, status) in $options.ALERT_STATUSES"
:key=
"status"
:is-checked=
"rule.status === status"
is-check-item
<gl-card
class=
"gl-border-gray-400 gl-bg-gray-10 gl-mb-3 gl-relative"
>
<gl-icon
v-if=
"index !== 0"
name=
"close"
class=
"gl-absolute rule-close-icon"
@
click=
"$emit('remove-escalation-rule', index)"
/>
<gl-form-group
:invalid-feedback=
"$options.i18n.fields.rules.validationMsg"
:state=
"isValid"
class=
"gl-mb-0"
>
<div
class=
"gl-display-flex gl-align-items-center"
>
<gl-sprintf
:message=
"$options.i18n.fields.rules.condition"
>
<template
#alertStatus
>
<gl-dropdown
class=
"rule-control gl-mx-3"
:text=
"$options.ALERT_STATUSES[status]"
data-testid=
"alert-status-dropdown"
>
<gl-dropdown-item
v-for=
"(label, alertStatus) in $options.ALERT_STATUSES"
:key=
"alertStatus"
:is-checked=
"status === alertStatus"
is-check-item
@
click=
"setStatus(alertStatus)"
>
{{
label
}}
</gl-dropdown-item>
</gl-dropdown>
</
template
>
<
template
#minutes
>
<gl-form-input
v-model=
"elapsedTimeSeconds"
class=
"gl-mx-3 gl-inset-border-1-gray-200! rule-elapsed-minutes"
type=
"number"
min=
"0"
@
change=
"emitUpdate"
/>
</
template
>
</gl-sprintf>
</div>
<div
class=
"gl-display-flex gl-align-items-center gl-mt-3"
>
<gl-sprintf
:message=
"$options.i18n.fields.rules.action"
>
<
template
#doAction
>
<gl-dropdown
class=
"rule-control gl-mx-3"
:text=
"$options.ACTIONS[rule.action]"
data-testid=
"action-dropdown"
>
{{
label
}}
</gl-dropdown-item>
</gl-dropdown>
</
template
>
<
template
#minutes
>
<gl-form-input
class=
"gl-mx-3 rule-elapsed-minutes"
:value=
"0"
/>
</
template
>
</gl-sprintf>
</div>
<div
class=
"gl-display-flex gl-align-items-center gl-mt-3"
>
<gl-sprintf
:message=
"$options.i18n.fields.rules.action"
>
<
template
#doAction
>
<gl-dropdown
class=
"rule-control gl-mx-3"
:text=
"$options.ACTIONS[rule.action]"
data-testid=
"action-dropdown"
>
<gl-dropdown-item
v-for=
"(label, action) in $options.ACTIONS"
:key=
"action"
:is-checked=
"rule.action === action"
is-check-item
<gl-dropdown-item
v-for=
"(label, ruleAction) in $options.ACTIONS"
:key=
"ruleAction"
:is-checked=
"rule.action === ruleAction"
is-check-item
>
{{
label
}}
</gl-dropdown-item>
</gl-dropdown>
</
template
>
<
template
#schedule
>
<gl-dropdown
class=
"rule-control"
:text=
"scheduleDropdownTitle"
data-testid=
"schedules-dropdown"
>
{{
label
}}
</gl-dropdown-item>
</gl-dropdown>
</
template
>
<
template
#schedule
>
<gl-dropdown
class=
"rule-control gl-mx-3"
:text=
"$options.i18n.fields.rules.selectSchedule"
data-testid=
"schedules-dropdown"
>
<gl-dropdown-item
v-for=
"schedule in schedules"
:key=
"schedule.id"
is-check-item
>
{{
schedule
.
name
}}
</gl-dropdown-item>
</gl-dropdown>
</
template
>
</gl-sprintf>
</div>
<template
#button-text
>
<span
:class=
"
{ 'gl-text-gray-400': !oncallScheduleIid }">
{{
scheduleDropdownTitle
}}
</span>
</
template
>
<gl-dropdown-item
v-for=
"schedule in schedules"
:key=
"schedule.iid"
:is-checked=
"schedule.iid === oncallScheduleIid"
is-check-item
@
click=
"setOncallSchedule(schedule)"
>
{{ schedule.name }}
</gl-dropdown-item>
</gl-dropdown>
</template>
</gl-sprintf>
</div>
</gl-form-group>
</gl-card>
</template>
ee/app/assets/javascripts/escalation_policies/constants.js
View file @
01673739
...
...
@@ -13,10 +13,7 @@ export const defaultEscalationRule = {
status
:
'
ACKNOWLEDGED
'
,
elapsedTimeSeconds
:
0
,
action
:
'
EMAIL_ONCALL_SCHEDULE_USER
'
,
oncallSchedule
:
{
iid
:
null
,
name
:
null
,
},
oncallScheduleIid
:
null
,
};
export
const
addEscalationPolicyModalId
=
'
addEscalationPolicyModal
'
;
ee/app/assets/javascripts/escalation_policies/graphql/mutations/create_escalation_policy.mutation.graphql
0 → 100644
View file @
01673739
mutation
escalationPolicyCreate
(
$input
:
EscalationPolicyCreateInput
!)
{
escalationPolicyCreate
(
input
:
$input
)
{
escalationPolicy
{
id
name
description
rules
{
status
elapsedTimeSeconds
oncallSchedule
{
iid
name
}
}
}
errors
}
}
ee/app/assets/javascripts/escalation_policies/graphql/queries/get_oncall_schedules.query.graphql
0 → 100644
View file @
01673739
query
getOncallSchedules
(
$projectPath
:
ID
!)
{
project
(
fullPath
:
$projectPath
)
{
incidentManagementOncallSchedules
{
nodes
{
iid
name
}
}
}
}
ee/app/assets/javascripts/escalation_policies/index.js
View file @
01673739
import
{
defaultDataIdFromObject
}
from
'
apollo-cache-inmemory
'
;
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
EscalationPoliciesWrapper
from
'
./components/escalation_policies_wrapper.vue
'
;
Vue
.
use
(
VueApollo
);
const
apolloProvider
=
new
VueApollo
({
defaultClient
:
createDefaultClient
(
{},
{
cacheConfig
:
{
dataIdFromObject
:
(
object
)
=>
{
// eslint-disable-next-line no-underscore-dangle
if
(
object
.
__typename
===
'
IncidentManagementOncallSchedule
'
)
{
return
object
.
iid
;
}
return
defaultDataIdFromObject
(
object
);
},
},
},
),
});
export
default
()
=>
{
const
el
=
document
.
querySelector
(
'
.js-escalation-policies
'
);
...
...
@@ -10,6 +32,7 @@ export default () => {
return
new
Vue
({
el
,
apolloProvider
,
provide
:
{
projectPath
,
emptyEscalationPoliciesSvgPath
,
...
...
ee/app/assets/javascripts/escalation_policies/utils.js
View file @
01673739
...
...
@@ -7,3 +7,13 @@
export
const
isNameFieldValid
=
(
name
)
=>
{
return
Boolean
(
name
?.
length
);
};
/**
* Returns an array of booleans - validation state for each rule
* @param {Array} rules
*
* @returns {Array}
*/
export
const
getRulesValidationState
=
(
rules
)
=>
{
return
rules
.
map
((
rule
)
=>
Boolean
(
rule
.
oncallScheduleIid
));
};
ee/app/helpers/incident_management/escalation_policy_helper.rb
View file @
01673739
...
...
@@ -2,9 +2,10 @@
module
IncidentManagement
module
EscalationPolicyHelper
def
escalation_policy_data
def
escalation_policy_data
(
project
)
{
'empty_escalation_policies_svg_path'
=>
image_path
(
'illustrations/empty-state/empty-escalation.svg'
)
'project-path'
=>
project
.
full_path
,
'empty_escalation_policies_svg_path'
=>
image_path
(
'illustrations/empty-state/empty-escalation.svg'
)
}
end
end
...
...
ee/app/views/projects/incident_management/escalation_policies/index.html.haml
View file @
01673739
-
page_title
_
(
'Escalation policies'
)
-
add_page_specific_style
'page_bundles/escalation_policies'
.js-escalation-policies
{
data:
escalation_policy_data
}
.js-escalation-policies
{
data:
escalation_policy_data
(
@project
)
}
ee/lib/ee/sidebars/projects/menus/monitor_menu.rb
View file @
01673739
...
...
@@ -38,7 +38,7 @@ module EE
end
::
Sidebars
::
MenuItem
.
new
(
title:
_
(
'Escalation
p
olicies'
),
title:
_
(
'Escalation
P
olicies'
),
link:
project_incident_management_escalation_policies_path
(
context
.
project
),
active_routes:
{
controller: :escalation_policies
},
item_id: :escalation_policies
...
...
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_form_spec.js
View file @
01673739
...
...
@@ -6,7 +6,6 @@ import AddEscalationPolicyForm, {
import
EscalationRule
from
'
ee/escalation_policies/components/escalation_rule.vue
'
;
import
{
defaultEscalationRule
}
from
'
ee/escalation_policies/constants
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
mockPolicy
from
'
./mocks/mockPolicy.json
'
;
describe
(
'
AddEscalationPolicyForm
'
,
()
=>
{
...
...
@@ -23,6 +22,7 @@ describe('AddEscalationPolicyForm', () => {
},
validationState
:
{
name
:
true
,
rules
:
[],
},
...
props
,
},
...
...
@@ -46,13 +46,15 @@ describe('AddEscalationPolicyForm', () => {
const
findAddRuleLink
=
()
=>
wrapper
.
findComponent
(
GlLink
);
describe
(
'
Escalation policy form validation
'
,
()
=>
{
it
(
'
should s
how feedback for an invalid name input validation state
'
,
async
()
=>
{
it
(
'
should s
et correct validation state for validated controls
'
,
async
()
=>
{
createComponent
({
props
:
{
validationState
:
{
name
:
false
},
validationState
:
{
name
:
false
,
rules
:
[
false
]
},
},
});
expect
(
findPolicyName
().
attributes
(
'
state
'
)).
toBeFalsy
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
findPolicyName
().
attributes
(
'
state
'
)).
toBeUndefined
();
expect
(
findRules
().
at
(
0
).
attributes
(
'
is-valid
'
)).
toBeUndefined
();
});
});
...
...
@@ -72,7 +74,40 @@ describe('AddEscalationPolicyForm', () => {
await
wrapper
.
vm
.
$nextTick
();
const
rules
=
findRules
();
expect
(
rules
.
length
).
toBe
(
2
);
expect
(
rules
.
at
(
1
).
props
(
'
rule
'
)).
toEqual
(
defaultEscalationRule
);
expect
(
rules
.
at
(
1
).
props
(
'
rule
'
)).
toMatchObject
(
defaultEscalationRule
);
});
it
(
'
should emit updates when rule is added
'
,
async
()
=>
{
findAddRuleLink
().
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
emitted
(
'
update-escalation-policy-form
'
)[
0
]).
toMatchObject
([
{
field
:
'
rules
'
,
value
:
[
expect
.
objectContaining
(
defaultEscalationRule
),
expect
.
objectContaining
(
defaultEscalationRule
),
],
},
]);
});
it
(
'
on rule update emitted should update rules array and emit updates up
'
,
()
=>
{
const
updatedRule
=
{
status
:
'
TRIGGERED
'
,
elapsedTimeSeconds
:
30
,
oncallScheduleIid
:
2
,
};
findRules
().
at
(
0
).
vm
.
$emit
(
'
update-escalation-rule
'
,
0
,
updatedRule
);
expect
(
wrapper
.
emitted
(
'
update-escalation-policy-form
'
)[
0
]).
toEqual
([
{
field
:
'
rules
'
,
value
:
[
updatedRule
]
},
]);
});
it
(
'
on rule removal emitted should update rules array and emit updates up
'
,
()
=>
{
findRules
().
at
(
0
).
vm
.
$emit
(
'
remove-escalation-rule
'
,
0
);
expect
(
wrapper
.
emitted
(
'
update-escalation-policy-form
'
)[
0
]).
toEqual
([
{
field
:
'
rules
'
,
value
:
[]
},
]);
});
});
});
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_modal_spec.js
View file @
01673739
import
{
GlModal
}
from
'
@gitlab/ui
'
;
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
AddEscalationPolicyForm
from
'
ee/escalation_policies/components/add_edit_escalation_policy_form.vue
'
;
import
AddEscalationPolicyModal
,
{
i18n
,
}
from
'
ee/escalation_policies/components/add_edit_escalation_policy_modal.vue
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
mockPolicy
from
'
./mocks/mockPolicy.json
'
;
describe
(
'
AddEscalationPolicyModal
'
,
()
=>
{
let
wrapper
;
const
projectPath
=
'
group/project
'
;
const
mockHideModal
=
jest
.
fn
();
const
mutate
=
jest
.
fn
();
const
createComponent
=
({
escalationPolicy
,
data
}
=
{})
=>
{
wrapper
=
shallowMount
(
AddEscalationPolicyModal
,
{
data
()
{
return
{
form
:
mockPolicy
,
...
data
,
};
},
...
...
@@ -22,7 +27,14 @@ describe('AddEscalationPolicyModal', () => {
provide
:
{
projectPath
,
},
mocks
:
{
$apollo
:
{
mutate
,
},
},
});
wrapper
.
vm
.
$refs
.
addUpdateEscalationPolicyModal
.
hide
=
mockHideModal
;
};
beforeEach
(()
=>
{
createComponent
();
...
...
@@ -34,6 +46,7 @@ describe('AddEscalationPolicyModal', () => {
const
findModal
=
()
=>
wrapper
.
findComponent
(
GlModal
);
const
findEscalationPolicyForm
=
()
=>
wrapper
.
findComponent
(
AddEscalationPolicyForm
);
const
findAlert
=
()
=>
wrapper
.
findComponent
(
GlAlert
);
describe
(
'
renders create modal with the correct information
'
,
()
=>
{
it
(
'
renders modal title
'
,
()
=>
{
...
...
@@ -43,6 +56,49 @@ describe('AddEscalationPolicyModal', () => {
it
(
'
renders the form inside the modal
'
,
()
=>
{
expect
(
findEscalationPolicyForm
().
exists
()).
toBe
(
true
);
});
it
(
'
makes a request with form data to create an escalation policy
'
,
()
=>
{
mutate
.
mockResolvedValueOnce
({});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
expect
(
mutate
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
variables
:
{
input
:
{
projectPath
,
...
mockPolicy
,
},
},
}),
);
});
it
(
'
hides the modal on successful policy creation
'
,
async
()
=>
{
mutate
.
mockResolvedValueOnce
({
data
:
{
escalationPolicyCreate
:
{
errors
:
[]
}
}
});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
await
waitForPromises
();
expect
(
mockHideModal
).
toHaveBeenCalled
();
});
it
(
"
doesn't hide a modal and shows error alert on creation failure
"
,
async
()
=>
{
const
error
=
'
some error
'
;
mutate
.
mockResolvedValueOnce
({
data
:
{
escalationPolicyCreate
:
{
errors
:
[
error
]
}
}
});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
await
waitForPromises
();
const
alert
=
findAlert
();
expect
(
mockHideModal
).
not
.
toHaveBeenCalled
();
expect
(
alert
.
exists
()).
toBe
(
true
);
expect
(
alert
.
text
()).
toContain
(
error
);
});
it
(
'
clears the form on modal close
'
,
()
=>
{
expect
(
wrapper
.
vm
.
form
).
toEqual
(
mockPolicy
);
findModal
().
vm
.
$emit
(
'
cancel
'
,
{
preventDefault
:
jest
.
fn
()
});
expect
(
wrapper
.
vm
.
form
).
toEqual
({
name
:
''
,
description
:
''
,
rules
:
[],
});
});
});
describe
(
'
modal buttons
'
,
()
=>
{
...
...
ee/spec/frontend/escalation_policies/escalation_rule_spec.js
View file @
01673739
import
{
GlDropdownItem
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
GlDropdownItem
,
Gl
FormGroup
,
Gl
Sprintf
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
EscalationRule
from
'
ee/escalation_policies/components/escalation_rule.vue
'
;
...
...
@@ -19,6 +19,8 @@ describe('EscalationRule', () => {
propsData
:
{
rule
:
cloneDeep
(
defaultEscalationRule
),
schedules
:
mockSchedules
,
index
:
0
,
isValid
:
false
,
...
props
,
},
stubs
:
{
...
...
@@ -45,6 +47,8 @@ describe('EscalationRule', () => {
const
findSchedulesDropdown
=
()
=>
wrapper
.
findByTestId
(
'
schedules-dropdown
'
);
const
findSchedulesDropdownOptions
=
()
=>
findSchedulesDropdown
().
findAll
(
GlDropdownItem
);
const
findFormGroup
=
()
=>
wrapper
.
findComponent
(
GlFormGroup
);
describe
(
'
Status dropdown
'
,
()
=>
{
it
(
'
should have correct alert status options
'
,
()
=>
{
expect
(
findStatusDropdownOptions
().
wrappers
.
map
((
w
)
=>
w
.
text
())).
toStrictEqual
(
...
...
@@ -76,4 +80,19 @@ describe('EscalationRule', () => {
);
});
});
describe
(
'
Validation
'
,
()
=>
{
it
.
each
`
isValid | state
${
true
}
|
${
'
true
'
}
${
false
}
|
${
undefined
}
`
(
'
when $isValid sets from group state to $state
'
,
({
isValid
,
state
})
=>
{
createComponent
({
props
:
{
isValid
,
},
});
expect
(
findFormGroup
().
attributes
(
'
state
'
)).
toBe
(
state
);
});
});
});
ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb
View file @
01673739
...
...
@@ -3,11 +3,14 @@
require
'spec_helper'
RSpec
.
describe
IncidentManagement
::
EscalationPolicyHelper
do
let_it_be
(
:project
)
{
create
(
:project
)
}
describe
'#escalation_policy_data'
do
subject
(
:data
)
{
helper
.
escalation_policy_data
}
subject
(
:data
)
{
helper
.
escalation_policy_data
(
project
)
}
it
'returns scalation policies data'
do
is_expected
.
to
eq
(
'project-path'
=>
project
.
full_path
,
'empty_escalation_policies_svg_path'
=>
helper
.
image_path
(
'illustrations/empty-state/empty-escalation.svg'
)
)
end
...
...
ee/spec/lib/ee/sidebars/projects/menus/monitor_menu_spec.rb
View file @
01673739
...
...
@@ -26,7 +26,7 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
end
end
describe
'Escalation
p
olicies'
do
describe
'Escalation
P
olicies'
do
let
(
:item_id
)
{
:escalation_policies
}
before
do
...
...
ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
View file @
01673739
...
...
@@ -275,7 +275,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe
'Escalation
p
olicies'
do
describe
'Escalation
P
olicies'
do
before
do
allow
(
view
).
to
receive
(
:current_user
).
and_return
(
user
)
stub_licensed_features
(
oncall_schedules:
true
,
escalation_policies:
true
)
...
...
@@ -284,7 +284,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it
'has a link to the escalation policies page'
do
render
expect
(
rendered
).
to
have_link
(
'Escalation
p
olicies'
,
href:
project_incident_management_escalation_policies_path
(
project
))
expect
(
rendered
).
to
have_link
(
'Escalation
P
olicies'
,
href:
project_incident_management_escalation_policies_path
(
project
))
end
describe
'when the user does not have access'
do
...
...
@@ -293,7 +293,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it
'does not have a link to the escalation policies page'
do
render
expect
(
rendered
).
not_to
have_link
(
'Escalation
p
olicies'
)
expect
(
rendered
).
not_to
have_link
(
'Escalation
P
olicies'
)
end
end
end
...
...
locale/gitlab.pot
View file @
01673739
...
...
@@ -13072,6 +13072,9 @@ msgstr ""
msgid "Errors:"
msgstr ""
msgid "Escalation Policies"
msgstr ""
msgid "Escalation policies"
msgstr ""
...
...
@@ -13081,6 +13084,9 @@ msgstr ""
msgid "EscalationPolicies|+ Add an additional rule"
msgstr ""
msgid "EscalationPolicies|A schedule is required for adding an escalation policy."
msgstr ""
msgid "EscalationPolicies|Add an escalation policy"
msgstr ""
...
...
@@ -13099,6 +13105,9 @@ msgstr ""
msgid "EscalationPolicies|Escalation rules"
msgstr ""
msgid "EscalationPolicies|Failed to load oncall-schedules"
msgstr ""
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
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