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
55f36fa5
Commit
55f36fa5
authored
Dec 02, 2020
by
Peter Leitzen
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '262847-add-schedule' into 'master'
Add a schedule modal See merge request gitlab-org/gitlab!48122
parents
8df40386
21b672cd
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
677 additions
and
61 deletions
+677
-61
app/assets/stylesheets/page_bundles/oncall_schedules.scss
app/assets/stylesheets/page_bundles/oncall_schedules.scss
+30
-0
app/helpers/ci/pipeline_schedules_helper.rb
app/helpers/ci/pipeline_schedules_helper.rb
+0
-15
app/helpers/time_zone_helper.rb
app/helpers/time_zone_helper.rb
+15
-0
config/application.rb
config/application.rb
+1
-0
ee/app/assets/javascripts/oncall_schedules/components/add_schedule_modal.vue
...cripts/oncall_schedules/components/add_schedule_modal.vue
+223
-0
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
.../oncall_schedules/components/oncall_schedules_wrapper.vue
+23
-14
ee/app/assets/javascripts/oncall_schedules/graphql/create_oncall_schedule.mutation.graphql
...schedules/graphql/create_oncall_schedule.mutation.graphql
+11
-0
ee/app/assets/javascripts/oncall_schedules/index.js
ee/app/assets/javascripts/oncall_schedules/index.js
+12
-1
ee/app/helpers/incident_management/oncall_schedule_helper.rb
ee/app/helpers/incident_management/oncall_schedule_helper.rb
+4
-2
ee/app/views/projects/incident_management/oncall_schedules/index.html.haml
...ects/incident_management/oncall_schedules/index.html.haml
+2
-1
ee/spec/frontend/oncall_schedule/__snapshots__/add_schedule_modal_spec.js.snap
...ll_schedule/__snapshots__/add_schedule_modal_spec.js.snap
+126
-0
ee/spec/frontend/oncall_schedule/add_schedule_modal_spec.js
ee/spec/frontend/oncall_schedule/add_schedule_modal_spec.js
+140
-0
ee/spec/frontend/oncall_schedule/mocks/mockTimezones.json
ee/spec/frontend/oncall_schedule/mocks/mockTimezones.json
+26
-0
ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb
...elpers/incident_management/oncall_schedule_helper_spec.rb
+4
-2
locale/gitlab.pot
locale/gitlab.pot
+21
-0
spec/frontend/fixtures/freeze_period.rb
spec/frontend/fixtures/freeze_period.rb
+4
-2
spec/helpers/ci/pipeline_schedules_helper_spec.rb
spec/helpers/ci/pipeline_schedules_helper_spec.rb
+0
-24
spec/helpers/time_zone_helper_spec.rb
spec/helpers/time_zone_helper_spec.rb
+35
-0
No files found.
app/assets/stylesheets/page_bundles/oncall_schedules.scss
0 → 100644
View file @
55f36fa5
@import
'mixins_and_variables_and_functions'
;
@mixin
inset-border-1-red-500
(
$important
:
false
)
{
box-shadow
:
inset
0
0
0
$gl-border-size-1
$red-500
if-important
(
$important
);
}
.timezone-dropdown
{
.dropdown-menu
{
@include
gl-w-full
;
}
.gl-new-dropdown-item-text-primary
{
@include
gl-overflow-hidden
;
@include
gl-text-overflow-ellipsis
;
}
}
.modal-footer
{
@include
gl-bg-gray-10
;
}
.invalid-dropdown
{
.gl-dropdown-toggle
{
@include
inset-border-1-red-500
;
&
:hover
{
@include
inset-border-1-red-500
(
true
);
}
}
}
app/helpers/ci/pipeline_schedules_helper.rb
deleted
100644 → 0
View file @
8df40386
# frozen_string_literal: true
module
Ci
module
PipelineSchedulesHelper
def
timezone_data
ActiveSupport
::
TimeZone
.
all
.
map
do
|
timezone
|
{
name:
timezone
.
name
,
offset:
timezone
.
now
.
utc_offset
,
identifier:
timezone
.
tzinfo
.
identifier
}
end
end
end
end
app/helpers/time_zone_helper.rb
0 → 100644
View file @
55f36fa5
# frozen_string_literal: true
module
TimeZoneHelper
def
timezone_data
ActiveSupport
::
TimeZone
.
all
.
map
do
|
timezone
|
{
identifier:
timezone
.
tzinfo
.
identifier
,
name:
timezone
.
name
,
abbr:
timezone
.
tzinfo
.
strftime
(
'%Z'
),
offset:
timezone
.
now
.
utc_offset
,
formatted_offset:
timezone
.
now
.
formatted_offset
}
end
end
end
config/application.rb
View file @
55f36fa5
...
...
@@ -203,6 +203,7 @@ module Gitlab
config
.
assets
.
precompile
<<
"page_bundles/wiki.css"
config
.
assets
.
precompile
<<
"page_bundles/xterm.css"
config
.
assets
.
precompile
<<
"page_bundles/alert_management_settings.css"
config
.
assets
.
precompile
<<
"page_bundles/oncall_schedules.css"
config
.
assets
.
precompile
<<
"lazy_bundles/cropper.css"
config
.
assets
.
precompile
<<
"lazy_bundles/select2.css"
config
.
assets
.
precompile
<<
"performance_bar.css"
...
...
ee/app/assets/javascripts/oncall_schedules/components/add_schedule_modal.vue
0 → 100644
View file @
55f36fa5
<
script
>
import
{
isEqual
,
isEmpty
}
from
'
lodash
'
;
import
{
GlModal
,
GlForm
,
GlFormGroup
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlSearchBoxByType
,
GlAlert
,
}
from
'
@gitlab/ui
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
createOncallScheduleMutation
from
'
../graphql/create_oncall_schedule.mutation.graphql
'
;
export
const
i18n
=
{
selectTimezone
:
s__
(
'
OnCallSchedules|Select timezone
'
),
search
:
__
(
'
Search
'
),
noResults
:
__
(
'
No matching results
'
),
cancel
:
__
(
'
Cancel
'
),
addSchedule
:
s__
(
'
OnCallSchedules|Add schedule
'
),
fields
:
{
name
:
{
title
:
__
(
'
Name
'
),
validation
:
{
empty
:
__
(
"
Can't be empty
"
),
},
},
description
:
{
title
:
__
(
'
Description (optional)
'
)
},
timezone
:
{
title
:
__
(
'
Timezone
'
),
description
:
s__
(
'
OnCallSchedules|Sets the default timezone for the schedule, for all participants
'
,
),
validation
:
{
empty
:
__
(
"
Can't be empty
"
),
},
},
},
errorMsg
:
s__
(
'
OnCallSchedules|Failed to add schedule
'
),
};
export
default
{
i18n
,
inject
:
[
'
projectPath
'
,
'
timezones
'
],
components
:
{
GlModal
,
GlForm
,
GlFormGroup
,
GlFormInput
,
GlDropdown
,
GlDropdownItem
,
GlSearchBoxByType
,
GlAlert
,
},
props
:
{
modalId
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
loading
:
false
,
tzSearchTerm
:
''
,
form
:
{
name
:
''
,
description
:
''
,
timezone
:
{},
},
error
:
null
,
};
},
computed
:
{
actionsProps
()
{
return
{
primary
:
{
text
:
i18n
.
addSchedule
,
attributes
:
[
{
variant
:
'
info
'
},
{
loading
:
this
.
loading
},
{
disabled
:
this
.
isFormInvalid
},
],
},
cancel
:
{
text
:
i18n
.
cancel
,
},
};
},
filteredTimezones
()
{
const
lowerCaseTzSearchTerm
=
this
.
tzSearchTerm
.
toLowerCase
();
return
this
.
timezones
.
filter
(
tz
=>
this
.
getFormattedTimezone
(
tz
)
.
toLowerCase
()
.
includes
(
lowerCaseTzSearchTerm
),
);
},
noResults
()
{
return
!
this
.
filteredTimezones
.
length
;
},
selectedTimezone
()
{
return
isEmpty
(
this
.
form
.
timezone
)
?
i18n
.
selectTimezone
:
this
.
getFormattedTimezone
(
this
.
form
.
timezone
);
},
isNameInvalid
()
{
return
!
this
.
form
.
name
.
length
;
},
isTimezoneInvalid
()
{
return
isEmpty
(
this
.
form
.
timezone
);
},
isFormInvalid
()
{
return
this
.
isNameInvalid
||
this
.
isTimezoneInvalid
;
},
},
methods
:
{
createSchedule
()
{
this
.
loading
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
createOncallScheduleMutation
,
variables
:
{
oncallScheduleCreateInput
:
{
projectPath
:
this
.
projectPath
,
...
this
.
form
,
timezone
:
this
.
form
.
timezone
.
identifier
,
},
},
})
.
then
(({
data
:
{
oncallScheduleCreate
:
{
errors
:
[
error
]
}
}
})
=>
{
if
(
error
)
{
throw
error
;
}
this
.
$refs
.
createScheduleModal
.
hide
();
})
.
catch
(
error
=>
{
this
.
error
=
error
;
})
.
finally
(()
=>
{
this
.
loading
=
false
;
});
},
setSelectedTimezone
(
tz
)
{
this
.
form
.
timezone
=
tz
;
},
getFormattedTimezone
(
tz
)
{
return
__
(
`(UTC
${
tz
.
formatted_offset
}
)
${
tz
.
abbr
}
${
tz
.
name
}
`
);
},
isTimezoneSelected
(
tz
)
{
return
isEqual
(
tz
,
this
.
form
.
timezone
);
},
hideErrorAlert
()
{
this
.
error
=
null
;
},
},
};
</
script
>
<
template
>
<gl-modal
ref=
"createScheduleModal"
:modal-id=
"modalId"
size=
"sm"
:title=
"$options.i18n.addSchedule"
:action-primary=
"actionsProps.primary"
:action-cancel=
"actionsProps.cancel"
@
primary.prevent=
"createSchedule"
>
<gl-alert
v-if=
"error"
variant=
"danger"
class=
"gl-mt-n3 gl-mb-3"
@
dismiss=
"hideErrorAlert"
>
{{
error
||
$options
.
i18n
.
errorMsg
}}
</gl-alert>
<gl-form>
<gl-form-group
:label=
"$options.i18n.fields.name.title"
:invalid-feedback=
"$options.i18n.fields.name.validation.empty"
label-size=
"sm"
label-for=
"schedule-name"
>
<gl-form-input
id=
"schedule-name"
v-model=
"form.name"
:state=
"!isNameInvalid"
/>
</gl-form-group>
<gl-form-group
:label=
"$options.i18n.fields.description.title"
label-size=
"sm"
label-for=
"schedule-description"
>
<gl-form-input
id=
"schedule-description"
v-model=
"form.description"
/>
</gl-form-group>
<gl-form-group
:label=
"$options.i18n.fields.timezone.title"
label-size=
"sm"
label-for=
"schedule-timezone"
:description=
"$options.i18n.fields.timezone.description"
:state=
"!isTimezoneInvalid"
:invalid-feedback=
"$options.i18n.fields.timezone.validation.empty"
>
<gl-dropdown
id=
"schedule-timezone"
:text=
"selectedTimezone"
class=
"timezone-dropdown gl-w-full"
:header-text=
"$options.i18n.selectTimezone"
:class=
"
{ 'invalid-dropdown': isTimezoneInvalid }"
>
<gl-search-box-by-type
v-model.trim=
"tzSearchTerm"
/>
<gl-dropdown-item
v-for=
"tz in filteredTimezones"
:key=
"getFormattedTimezone(tz)"
:is-checked=
"isTimezoneSelected(tz)"
is-check-item
@
click=
"setSelectedTimezone(tz)"
>
<span
class=
"gl-white-space-nowrap"
>
{{
getFormattedTimezone
(
tz
)
}}
</span>
</gl-dropdown-item>
<gl-dropdown-item
v-if=
"noResults"
>
{{
$options
.
i18n
.
noResults
}}
</gl-dropdown-item>
</gl-dropdown>
</gl-form-group>
</gl-form>
</gl-modal>
</
template
>
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedules_wrapper.vue
View file @
55f36fa5
<
script
>
import
{
GlEmptyState
,
GlButton
}
from
'
@gitlab/ui
'
;
import
{
GlEmptyState
,
GlButton
,
GlModalDirective
}
from
'
@gitlab/ui
'
;
import
AddScheduleModal
from
'
./add_schedule_modal.vue
'
;
import
{
s__
}
from
'
~/locale
'
;
const
addScheduleModalId
=
'
addScheduleModal
'
;
export
const
i18n
=
{
emptyState
:
{
title
:
s__
(
'
OnCallSchedules|Create on-call schedules in GitLab
'
),
...
...
@@ -12,27 +15,33 @@ export const i18n = {
export
default
{
i18n
,
addScheduleModalId
,
inject
:
[
'
emptyOncallSchedulesSvgPath
'
],
components
:
{
GlEmptyState
,
GlButton
,
AddScheduleModal
,
},
method
s
:
{
createSchedule
()
{}
,
directive
s
:
{
GlModal
:
GlModalDirective
,
},
methods
:
{},
};
</
script
>
<
template
>
<gl-empty-state
:title=
"$options.i18n.emptyState.title"
:description=
"$options.i18n.emptyState.description"
:svg-path=
"emptyOncallSchedulesSvgPath"
>
<template
#actions
>
<gl-button
variant=
"info"
@
click=
"createSchedule"
>
{{
$options
.
i18n
.
emptyState
.
button
}}
</gl-button>
</
template
>
</gl-empty-state>
<div>
<gl-empty-state
:title=
"$options.i18n.emptyState.title"
:description=
"$options.i18n.emptyState.description"
:svg-path=
"emptyOncallSchedulesSvgPath"
>
<template
#actions
>
<gl-button
v-gl-modal=
"$options.addScheduleModalId"
variant=
"info"
>
{{
$options
.
i18n
.
emptyState
.
button
}}
</gl-button>
</
template
>
</gl-empty-state>
<add-schedule-modal
:modal-id=
"$options.addScheduleModalId"
/>
</div>
</template>
ee/app/assets/javascripts/oncall_schedules/graphql/create_oncall_schedule.mutation.graphql
0 → 100644
View file @
55f36fa5
mutation
oncallScheduleCreate
(
$oncallScheduleCreateInput
:
OncallScheduleCreateInput
!)
{
oncallScheduleCreate
(
input
:
$oncallScheduleCreateInput
)
{
errors
oncallSchedule
{
iid
name
description
timezone
}
}
}
ee/app/assets/javascripts/oncall_schedules/index.js
View file @
55f36fa5
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
OnCallSchedulesWrapper
from
'
./components/oncall_schedules_wrapper.vue
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
Vue
.
use
(
VueApollo
);
export
default
()
=>
{
const
el
=
document
.
querySelector
(
'
#js-oncall_schedule
'
);
if
(
!
el
)
return
null
;
const
{
emptyOncallSchedulesSvgPath
}
=
el
.
dataset
;
const
{
projectPath
,
emptyOncallSchedulesSvgPath
,
timezones
}
=
el
.
dataset
;
const
apolloProvider
=
new
VueApollo
({
defaultClient
:
createDefaultClient
(),
});
return
new
Vue
({
el
,
apolloProvider
,
provide
:
{
projectPath
,
emptyOncallSchedulesSvgPath
,
timezones
:
JSON
.
parse
(
timezones
),
},
render
(
createElement
)
{
return
createElement
(
OnCallSchedulesWrapper
);
...
...
ee/app/helpers/incident_management/oncall_schedule_helper.rb
View file @
55f36fa5
...
...
@@ -2,9 +2,11 @@
module
IncidentManagement
module
OncallScheduleHelper
def
oncall_schedule_data
def
oncall_schedule_data
(
project
)
{
'empty-oncall-schedules-svg-path'
=>
image_path
(
'illustrations/empty-state/empty-on-call.svg'
)
'project-path'
=>
project
.
full_path
,
'empty-oncall-schedules-svg-path'
=>
image_path
(
'illustrations/empty-state/empty-on-call.svg'
),
'timezones'
=>
timezone_data
.
to_json
}
end
end
...
...
ee/app/views/projects/incident_management/oncall_schedules/index.html.haml
View file @
55f36fa5
-
page_title
_
(
'On-call schedules'
)
-
add_page_specific_style
'page_bundles/oncall_schedules'
#js-oncall_schedule
{
data:
oncall_schedule_data
}
#js-oncall_schedule
{
data:
oncall_schedule_data
(
@project
)
}
ee/spec/frontend/oncall_schedule/__snapshots__/add_schedule_modal_spec.js.snap
0 → 100644
View file @
55f36fa5
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AddScheduleModal renders modal layout 1`] = `
<gl-modal-stub
actioncancel="[object Object]"
actionprimary="[object Object]"
modalclass=""
modalid="modalId"
size="sm"
title="Add schedule"
titletag="h4"
>
<!---->
<gl-form-stub>
<gl-form-group-stub
invalid-feedback="Can't be empty"
label="Name"
label-for="schedule-name"
label-size="sm"
>
<gl-form-input-stub
id="schedule-name"
value=""
/>
</gl-form-group-stub>
<gl-form-group-stub
label="Description (optional)"
label-for="schedule-description"
label-size="sm"
>
<gl-form-input-stub
id="schedule-description"
value=""
/>
</gl-form-group-stub>
<gl-form-group-stub
description="Sets the default timezone for the schedule, for all participants"
invalid-feedback="Can't be empty"
label="Timezone"
label-for="schedule-timezone"
label-size="sm"
>
<gl-dropdown-stub
category="primary"
class="timezone-dropdown gl-w-full invalid-dropdown"
headertext="Select timezone"
id="schedule-timezone"
size="medium"
text="Select timezone"
variant="default"
>
<gl-search-box-by-type-stub
clearbuttontitle="Clear"
value=""
/>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
(UTC-12:00) -12 International Date Line West
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
(UTC-11:00) SST American Samoa
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
(UTC-11:00) SST Midway Island
</span>
</gl-dropdown-item-stub>
<gl-dropdown-item-stub
avatarurl=""
iconcolor=""
iconname=""
iconrightarialabel=""
iconrightname=""
ischeckitem="true"
secondarytext=""
>
<span
class="gl-white-space-nowrap"
>
(UTC-10:00) HST Hawaii
</span>
</gl-dropdown-item-stub>
<!---->
</gl-dropdown-stub>
</gl-form-group-stub>
</gl-form-stub>
</gl-modal-stub>
`;
ee/spec/frontend/oncall_schedule/add_schedule_modal_spec.js
0 → 100644
View file @
55f36fa5
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlSearchBoxByType
,
GlDropdown
,
GlDropdownItem
,
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
AddScheduleModal
,
{
i18n
}
from
'
ee/oncall_schedules/components/add_schedule_modal.vue
'
;
import
mockTimezones
from
'
./mocks/mockTimezones.json
'
;
describe
(
'
AddScheduleModal
'
,
()
=>
{
let
wrapper
;
const
projectPath
=
'
group/project
'
;
const
mutate
=
jest
.
fn
();
const
mockHideModal
=
jest
.
fn
();
function
mountComponent
()
{
wrapper
=
shallowMount
(
AddScheduleModal
,
{
propsData
:
{
modalId
:
'
modalId
'
,
},
provide
:
{
projectPath
,
timezones
:
mockTimezones
,
},
mocks
:
{
$apollo
:
{
mutate
,
},
},
stubs
:
{
GlFormGroup
:
false
,
},
});
wrapper
.
vm
.
$refs
.
createScheduleModal
.
hide
=
mockHideModal
;
}
beforeEach
(()
=>
{
mountComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
const
findModal
=
()
=>
wrapper
.
find
(
GlModal
);
const
findAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
findTimezoneDropdown
=
()
=>
wrapper
.
find
(
GlDropdown
);
const
findDropdownOptions
=
()
=>
wrapper
.
findAll
(
GlDropdownItem
);
const
findTimezoneSearchBox
=
()
=>
wrapper
.
find
(
GlSearchBoxByType
);
it
(
'
renders modal layout
'
,
()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
describe
(
'
Timezone select
'
,
()
=>
{
it
(
'
has options based on provided BE data
'
,
()
=>
{
expect
(
findDropdownOptions
().
length
).
toBe
(
mockTimezones
.
length
);
});
it
(
'
formats each option
'
,
()
=>
{
findDropdownOptions
().
wrappers
.
forEach
((
option
,
index
)
=>
{
const
tz
=
mockTimezones
[
index
];
const
expectedValue
=
`(UTC
${
tz
.
formatted_offset
}
)
${
tz
.
abbr
}
${
tz
.
name
}
`
;
expect
(
option
.
text
()).
toBe
(
expectedValue
);
});
});
describe
(
'
timezones filtering
'
,
()
=>
{
it
(
'
should filter options based on search term
'
,
async
()
=>
{
const
searchTerm
=
'
Hawaii
'
;
findTimezoneSearchBox
().
vm
.
$emit
(
'
input
'
,
searchTerm
);
await
wrapper
.
vm
.
$nextTick
();
const
options
=
findDropdownOptions
();
expect
(
options
.
length
).
toBe
(
1
);
expect
(
options
.
at
(
0
).
text
()).
toContain
(
searchTerm
);
});
it
(
'
should display no results item when there are no filter matches
'
,
async
()
=>
{
const
searchTerm
=
'
someUnexistentTZ
'
;
findTimezoneSearchBox
().
vm
.
$emit
(
'
input
'
,
searchTerm
);
await
wrapper
.
vm
.
$nextTick
();
const
options
=
findDropdownOptions
();
expect
(
options
.
length
).
toBe
(
1
);
expect
(
options
.
at
(
0
).
text
()).
toContain
(
i18n
.
noResults
);
});
});
it
(
'
should add a checkmark to the selected option
'
,
async
()
=>
{
const
selectedTZOption
=
findDropdownOptions
().
at
(
0
);
selectedTZOption
.
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
selectedTZOption
.
attributes
(
'
ischecked
'
)).
toBe
(
'
true
'
);
});
});
describe
(
'
Schedule create
'
,
()
=>
{
it
(
'
makes a request with form data to create a schedule
'
,
()
=>
{
mutate
.
mockResolvedValueOnce
({});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
expect
(
mutate
).
toHaveBeenCalledWith
({
mutation
:
expect
.
any
(
Object
),
variables
:
{
oncallScheduleCreateInput
:
expect
.
objectContaining
({
projectPath
})
},
});
});
it
(
'
hides the modal on successful schedule creation
'
,
async
()
=>
{
mutate
.
mockResolvedValueOnce
({
data
:
{
oncallScheduleCreate
:
{
errors
:
[]
}
}
});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
await
waitForPromises
();
expect
(
mockHideModal
).
toHaveBeenCalled
();
});
it
(
"
doesn't hide a modal and shows error alert on fail
"
,
async
()
=>
{
const
error
=
'
some error
'
;
mutate
.
mockResolvedValueOnce
({
data
:
{
oncallScheduleCreate
:
{
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
);
});
});
describe
(
'
Form validation
'
,
()
=>
{
describe
(
'
Timezone select
'
,
()
=>
{
it
(
'
has red border when nothing selected
'
,
()
=>
{
expect
(
findTimezoneDropdown
().
classes
()).
toContain
(
'
invalid-dropdown
'
);
});
it
(
"
doesn't have a red border when there is selected opeion
"
,
async
()
=>
{
findDropdownOptions
()
.
at
(
1
)
.
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
findTimezoneDropdown
().
classes
()).
not
.
toContain
(
'
invalid-dropdown
'
);
});
});
});
});
ee/spec/frontend/oncall_schedule/mocks/mockTimezones.json
0 → 100644
View file @
55f36fa5
[
{
"identifier"
:
"Etc/GMT+12"
,
"name"
:
"International Date Line West"
,
"abbr"
:
"-12"
,
"formatted_offset"
:
"-12:00"
},
{
"identifier"
:
"Pacific/Pago_Pago"
,
"name"
:
"American Samoa"
,
"abbr"
:
"SST"
,
"formatted_offset"
:
"-11:00"
},
{
"identifier"
:
"Pacific/Midway"
,
"name"
:
"Midway Island"
,
"abbr"
:
"SST"
,
"formatted_offset"
:
"-11:00"
},
{
"identifier"
:
"Pacific/Honolulu"
,
"name"
:
"Hawaii"
,
"abbr"
:
"HST"
,
"formatted_offset"
:
"-10:00"
}
]
ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb
View file @
55f36fa5
...
...
@@ -6,11 +6,13 @@ RSpec.describe IncidentManagement::OncallScheduleHelper do
let_it_be
(
:project
)
{
create
(
:project
)
}
describe
'#oncall_schedule_data'
do
subject
(
:data
)
{
helper
.
oncall_schedule_data
}
subject
(
:data
)
{
helper
.
oncall_schedule_data
(
project
)
}
it
'returns on-call schedule data'
do
is_expected
.
to
eq
(
'empty-oncall-schedules-svg-path'
=>
helper
.
image_path
(
'illustrations/empty-state/empty-on-call.svg'
)
'project-path'
=>
project
.
full_path
,
'empty-oncall-schedules-svg-path'
=>
helper
.
image_path
(
'illustrations/empty-state/empty-on-call.svg'
),
'timezones'
=>
helper
.
timezone_data
.
to_json
)
end
end
...
...
locale/gitlab.pot
View file @
55f36fa5
...
...
@@ -4881,6 +4881,9 @@ msgstr ""
msgid "Can't apply this suggestion."
msgstr ""
msgid "Can't be empty"
msgstr ""
msgid "Can't create snippet: %{err}"
msgstr ""
...
...
@@ -9348,6 +9351,9 @@ msgstr ""
msgid "Description"
msgstr ""
msgid "Description (optional)"
msgstr ""
msgid "Description parsed with %{link_start}GitLab Flavored Markdown%{link_end}"
msgstr ""
...
...
@@ -19033,12 +19039,24 @@ msgstr ""
msgid "OnCallSchedules|Add a schedule"
msgstr ""
msgid "OnCallSchedules|Add schedule"
msgstr ""
msgid "OnCallSchedules|Create on-call schedules in GitLab"
msgstr ""
msgid "OnCallSchedules|Failed to add schedule"
msgstr ""
msgid "OnCallSchedules|Route alerts directly to specific members of your team"
msgstr ""
msgid "OnCallSchedules|Select timezone"
msgstr ""
msgid "OnCallSchedules|Sets the default timezone for the schedule, for all participants"
msgstr ""
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
...
...
@@ -28436,6 +28454,9 @@ msgstr ""
msgid "Timeout connecting to the Google API. Please try again."
msgstr ""
msgid "Timezone"
msgstr ""
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
...
...
spec/frontend/fixtures/freeze_period.rb
View file @
55f36fa5
...
...
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec
.
describe
'Freeze Periods (JavaScript fixtures)'
do
include
JavaScriptFixturesHelpers
include
Ci
::
PipelineSchedules
Helper
include
TimeZone
Helper
let_it_be
(
:admin
)
{
create
(
:admin
)
}
let_it_be
(
:project
)
{
create
(
:project
,
:repository
,
path:
'freeze-periods-project'
)
}
...
...
@@ -40,10 +40,12 @@ RSpec.describe 'Freeze Periods (JavaScript fixtures)' do
end
end
describe
Ci
::
PipelineSchedules
Helper
,
'(JavaScript fixtures)'
do
describe
TimeZone
Helper
,
'(JavaScript fixtures)'
do
let
(
:response
)
{
timezone_data
.
to_json
}
it
'api/freeze-periods/timezone_data.json'
do
# Looks empty but does things
# More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38525/diffs#note_391048415
end
end
end
spec/helpers/ci/pipeline_schedules_helper_spec.rb
deleted
100644 → 0
View file @
8df40386
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Ci
::
PipelineSchedulesHelper
,
:aggregate_failures
do
describe
'#timezone_data'
do
subject
{
helper
.
timezone_data
}
it
'matches schema'
do
expect
(
subject
).
not_to
be_empty
subject
.
each_with_index
do
|
timzone_hash
,
i
|
expect
(
timzone_hash
.
keys
).
to
contain_exactly
(
:name
,
:offset
,
:identifier
),
"Failed at index
#{
i
}
"
end
end
it
'formats for display'
do
first_timezone
=
ActiveSupport
::
TimeZone
.
all
[
0
]
expect
(
subject
[
0
][
:name
]).
to
eq
(
first_timezone
.
name
)
expect
(
subject
[
0
][
:offset
]).
to
eq
(
first_timezone
.
now
.
utc_offset
)
expect
(
subject
[
0
][
:identifier
]).
to
eq
(
first_timezone
.
tzinfo
.
identifier
)
end
end
end
spec/helpers/time_zone_helper_spec.rb
0 → 100644
View file @
55f36fa5
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
TimeZoneHelper
,
:aggregate_failures
do
describe
'#timezone_data'
do
subject
(
:timezone_data
)
{
helper
.
timezone_data
}
it
'matches schema'
do
expect
(
timezone_data
).
not_to
be_empty
timezone_data
.
each_with_index
do
|
timezone_hash
,
i
|
expect
(
timezone_hash
.
keys
).
to
contain_exactly
(
:identifier
,
:name
,
:abbr
,
:offset
,
:formatted_offset
),
"Failed at index
#{
i
}
"
end
end
it
'formats for display'
do
tz
=
ActiveSupport
::
TimeZone
.
all
[
0
]
expect
(
timezone_data
[
0
]).
to
eq
(
identifier:
tz
.
tzinfo
.
identifier
,
name:
tz
.
name
,
abbr:
tz
.
tzinfo
.
strftime
(
'%Z'
),
offset:
tz
.
now
.
utc_offset
,
formatted_offset:
tz
.
now
.
formatted_offset
)
end
end
end
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