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
9ba1176e
Commit
9ba1176e
authored
Nov 02, 2020
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
e4515e07
263a9980
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
636 additions
and
15 deletions
+636
-15
app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
.../vue_shared/components/sidebar/issuable_move_dropdown.vue
+211
-0
app/assets/stylesheets/pages/issuable.scss
app/assets/stylesheets/pages/issuable.scss
+19
-0
app/services/projects/container_repository/delete_tags_service.rb
...ices/projects/container_repository/delete_tags_service.rb
+3
-3
app/services/projects/container_repository/gitlab/delete_tags_service.rb
...ojects/container_repository/gitlab/delete_tags_service.rb
+7
-4
changelogs/unreleased/delete_tags_service_logging_enhancement.yml
...gs/unreleased/delete_tags_service_logging_enhancement.yml
+6
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
..._shared/components/sidebar/issuable_move_dropdown_spec.js
+375
-0
spec/services/projects/container_repository/delete_tags_service_spec.rb
...projects/container_repository/delete_tags_service_spec.rb
+11
-7
spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
...s/container_repository/gitlab/delete_tags_service_spec.rb
+1
-1
No files found.
app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
0 → 100644
View file @
9ba1176e
<
script
>
import
{
GlIcon
,
GlLoadingIcon
,
GlDropdown
,
GlDropdownForm
,
GlDropdownItem
,
GlSearchBoxByType
,
GlButton
,
GlTooltipDirective
as
GlTooltip
,
}
from
'
@gitlab/ui
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
export
default
{
components
:
{
GlIcon
,
GlLoadingIcon
,
GlDropdown
,
GlDropdownForm
,
GlDropdownItem
,
GlSearchBoxByType
,
GlButton
,
},
directives
:
{
GlTooltip
,
},
props
:
{
projectsFetchPath
:
{
type
:
String
,
required
:
true
,
},
dropdownButtonTitle
:
{
type
:
String
,
required
:
true
,
},
dropdownHeaderTitle
:
{
type
:
String
,
required
:
true
,
},
moveInProgress
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
projectsListLoading
:
false
,
projectsListLoadFailed
:
false
,
searchKey
:
''
,
projects
:
[],
selectedProject
:
null
,
projectItemClick
:
false
,
};
},
computed
:
{
hasNoSearchResults
()
{
return
Boolean
(
!
this
.
projectsListLoading
&&
!
this
.
projectsListLoadFailed
&&
this
.
searchKey
&&
!
this
.
projects
.
length
,
);
},
failedToLoadResults
()
{
return
!
this
.
projectsListLoading
&&
this
.
projectsListLoadFailed
;
},
},
watch
:
{
searchKey
(
value
=
''
)
{
this
.
fetchProjects
(
value
);
},
},
methods
:
{
fetchProjects
(
search
=
''
)
{
this
.
projectsListLoading
=
true
;
this
.
projectsListLoadFailed
=
false
;
return
axios
.
get
(
this
.
projectsFetchPath
,
{
params
:
{
search
,
},
})
.
then
(({
data
})
=>
{
this
.
projects
=
data
;
this
.
$refs
.
searchInput
.
focusInput
();
})
.
catch
(()
=>
{
this
.
projectsListLoadFailed
=
true
;
})
.
finally
(()
=>
{
this
.
projectsListLoading
=
false
;
});
},
isSelectedProject
(
project
)
{
if
(
this
.
selectedProject
)
{
return
this
.
selectedProject
.
id
===
project
.
id
;
}
return
false
;
},
/**
* This handler is to prevent dropdown
* from closing when an item is selected
* and emit an event only when dropdown closes.
*/
handleDropdownHide
(
e
)
{
if
(
this
.
projectItemClick
)
{
e
.
preventDefault
();
this
.
projectItemClick
=
false
;
}
else
{
this
.
$emit
(
'
dropdown-close
'
);
}
},
handleDropdownCloseClick
()
{
this
.
$refs
.
dropdown
.
hide
();
},
handleProjectSelect
(
project
)
{
this
.
selectedProject
=
project
.
id
===
this
.
selectedProject
?.
id
?
null
:
project
;
this
.
projectItemClick
=
true
;
},
handleMoveClick
()
{
this
.
$refs
.
dropdown
.
hide
();
this
.
$emit
(
'
move-issuable
'
,
this
.
selectedProject
);
},
},
};
</
script
>
<
template
>
<div
class=
"block js-issuable-move-block issuable-move-dropdown sidebar-move-issue-dropdown"
>
<div
v-gl-tooltip
.
left
.
viewport
data-testid=
"move-collapsed"
:title=
"dropdownButtonTitle"
class=
"sidebar-collapsed-icon"
@
click=
"$emit('toggle-collapse')"
>
<gl-icon
name=
"arrow-right"
/>
</div>
<gl-dropdown
ref=
"dropdown"
:block=
"true"
:disabled=
"moveInProgress"
class=
"hide-collapsed"
toggle-class=
"js-sidebar-dropdown-toggle"
@
shown=
"fetchProjects"
@
hide=
"handleDropdownHide"
>
<template
#button-content
><gl-loading-icon
v-if=
"moveInProgress"
class=
"gl-mr-3"
/>
{{
dropdownButtonTitle
}}
</
template
>
<gl-dropdown-form
class=
"gl-pt-0"
>
<div
data-testid=
"header"
class=
"gl-display-flex gl-pb-3 gl-border-1 gl-border-b-solid gl-border-gray-100"
>
<span
class=
"gl-flex-grow-1 gl-text-center gl-font-weight-bold gl-py-1"
>
{{
dropdownHeaderTitle
}}
</span>
<gl-button
variant=
"link"
icon=
"close"
class=
"gl-mr-2 gl-w-auto! gl-p-2!"
@
click.prevent=
"handleDropdownCloseClick"
/>
</div>
<gl-search-box-by-type
ref=
"searchInput"
v-model.trim=
"searchKey"
:placeholder=
"__('Search project')"
:debounce=
"300"
/>
<div
data-testid=
"content"
class=
"dropdown-content"
>
<gl-loading-icon
v-if=
"projectsListLoading"
size=
"md"
class=
"gl-p-5"
/>
<ul
v-else
>
<gl-dropdown-item
v-for=
"project in projects"
:key=
"project.id"
:is-check-item=
"true"
:is-checked=
"isSelectedProject(project)"
@
click.stop.prevent=
"handleProjectSelect(project)"
>
{{ project.name_with_namespace }}
</gl-dropdown-item
>
</ul>
<div
v-if=
"hasNoSearchResults"
class=
"gl-text-center gl-p-3"
>
{{ __('No matching results') }}
</div>
<div
v-if=
"failedToLoadResults"
class=
"gl-text-center gl-p-3"
>
{{ __('Failed to load projects') }}
</div>
</div>
<div
data-testid=
"footer"
class=
"gl-pt-3 gl-px-3 gl-border-1 gl-border-t-solid gl-border-gray-100"
>
<gl-button
category=
"primary"
variant=
"success"
:disabled=
"!Boolean(selectedProject)"
class=
"gl-text-center! issuable-move-button"
@
click=
"handleMoveClick"
>
{{ __('Move') }}
</gl-button
>
</div>
</gl-dropdown-form>
</gl-dropdown>
</div>
</template>
app/assets/stylesheets/pages/issuable.scss
View file @
9ba1176e
...
...
@@ -921,6 +921,25 @@
}
}
/*
* Following overrides are done to prevent
* legacy dropdown styles from influencing
* GitLab UI components used within GlDropdown
*/
.issuable-move-dropdown
{
.b-dropdown-form
{
@include
gl-p-0
;
}
.gl-search-box-by-type
button
.gl-clear-icon-button
:hover
{
@include
gl-bg-transparent
;
}
.issuable-move-button
:not
(
.disabled
)
:hover
{
@include
gl-text-white
;
}
}
.right-sidebar-collapsed
{
.sidebar-grouped-item
{
.sidebar-collapsed-icon
{
...
...
app/services/projects/container_repository/delete_tags_service.rb
View file @
9ba1176e
...
...
@@ -36,11 +36,11 @@ module Projects
def
log_response
(
response
)
log_data
=
LOG_DATA_BASE
.
merge
(
container_repository_id:
@container_repository
.
id
,
message:
'deleted tags'
)
message:
'deleted tags'
,
deleted_tags_count:
response
[
:deleted
]
&
.
size
).
compact
if
response
[
:status
]
==
:success
log_data
[
:deleted_tags_count
]
=
response
[
:deleted
].
size
log_info
(
log_data
)
else
log_data
[
:message
]
=
response
[
:message
]
...
...
app/services/projects/container_repository/gitlab/delete_tags_service.rb
View file @
9ba1176e
...
...
@@ -14,6 +14,7 @@ module Projects
def
initialize
(
container_repository
,
tag_names
)
@container_repository
=
container_repository
@tag_names
=
tag_names
@deleted_tags
=
[]
end
# Delete tags by name with a single DELETE request. This is only supported
...
...
@@ -25,7 +26,7 @@ module Projects
delete_tags
rescue
TimeoutError
=>
e
::
Gitlab
::
ErrorTracking
.
track_exception
(
e
,
tags_count:
@tag_names
&
.
size
,
container_repository_id:
@container_repository
&
.
id
)
error
(
'timeout while deleting tags'
)
error
(
'timeout while deleting tags'
,
nil
,
pass_back:
{
deleted:
@deleted_tags
}
)
end
private
...
...
@@ -33,13 +34,15 @@ module Projects
def
delete_tags
start_time
=
Time
.
zone
.
now
deleted_tags
=
@tag_names
.
select
do
|
name
|
@tag_names
.
each
do
|
name
|
raise
TimeoutError
if
timeout?
(
start_time
)
@container_repository
.
delete_tag_by_name
(
name
)
if
@container_repository
.
delete_tag_by_name
(
name
)
@deleted_tags
.
append
(
name
)
end
end
deleted_tags
.
any?
?
success
(
deleted:
deleted_tags
)
:
error
(
'could not delete tags'
)
@deleted_tags
.
any?
?
success
(
deleted:
@
deleted_tags
)
:
error
(
'could not delete tags'
)
end
def
timeout?
(
start_time
)
...
...
changelogs/unreleased/delete_tags_service_logging_enhancement.yml
0 → 100644
View file @
9ba1176e
---
title
:
Improving Container Registry Delete Tags Service to log number of successfully
deleted tags even if deletion process was interrupted by a timeout
merge_request
:
46079
author
:
Maksim Stankevic, @maksimstankevic
type
:
changed
locale/gitlab.pot
View file @
9ba1176e
...
...
@@ -11139,6 +11139,9 @@ msgstr ""
msgid "Failed to load milestones. Please try again."
msgstr ""
msgid "Failed to load projects"
msgstr ""
msgid "Failed to load related branches"
msgstr ""
...
...
spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js
0 → 100644
View file @
9ba1176e
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
{
GlIcon
,
GlLoadingIcon
,
GlDropdown
,
GlDropdownForm
,
GlDropdownItem
,
GlSearchBoxByType
,
GlButton
,
}
from
'
@gitlab/ui
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
IssuableMoveDropdown
from
'
~/vue_shared/components/sidebar/issuable_move_dropdown.vue
'
;
const
mockProjects
=
[
{
id
:
2
,
name_with_namespace
:
'
Gitlab Org / Gitlab Shell
'
,
full_path
:
'
gitlab-org/gitlab-shell
'
,
},
{
id
:
3
,
name_with_namespace
:
'
Gnuwget / Wget2
'
,
full_path
:
'
gnuwget/wget2
'
,
},
{
id
:
4
,
name_with_namespace
:
'
Commit451 / Lab Coat
'
,
full_path
:
'
Commit451/lab-coat
'
,
},
];
const
mockProps
=
{
projectsFetchPath
:
'
/-/autocomplete/projects?project_id=1
'
,
dropdownButtonTitle
:
'
Move issuable
'
,
dropdownHeaderTitle
:
'
Move issuable
'
,
moveInProgress
:
false
,
};
const
mockEvent
=
{
stopPropagation
:
jest
.
fn
(),
preventDefault
:
jest
.
fn
(),
};
const
createComponent
=
(
propsData
=
mockProps
)
=>
shallowMount
(
IssuableMoveDropdown
,
{
propsData
,
});
describe
(
'
IssuableMoveDropdown
'
,
()
=>
{
let
mock
;
let
wrapper
;
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
wrapper
=
createComponent
();
wrapper
.
vm
.
$refs
.
dropdown
.
hide
=
jest
.
fn
();
wrapper
.
vm
.
$refs
.
searchInput
.
focusInput
=
jest
.
fn
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
mock
.
restore
();
});
describe
(
'
watch
'
,
()
=>
{
describe
(
'
searchKey
'
,
()
=>
{
it
(
'
calls `fetchProjects` with value of the prop
'
,
async
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
,
'
fetchProjects
'
);
wrapper
.
setData
({
searchKey
:
'
foo
'
,
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
fetchProjects
).
toHaveBeenCalledWith
(
'
foo
'
);
});
});
});
describe
(
'
methods
'
,
()
=>
{
describe
(
'
fetchProjects
'
,
()
=>
{
it
(
'
sets projectsListLoading to true and projectsListLoadFailed to false
'
,
()
=>
{
wrapper
.
vm
.
fetchProjects
();
expect
(
wrapper
.
vm
.
projectsListLoading
).
toBe
(
true
);
expect
(
wrapper
.
vm
.
projectsListLoadFailed
).
toBe
(
false
);
});
it
(
'
calls `axios.get` with `projectsFetchPath` and query param `search`
'
,
()
=>
{
jest
.
spyOn
(
axios
,
'
get
'
).
mockResolvedValue
({
data
:
mockProjects
,
});
wrapper
.
vm
.
fetchProjects
(
'
foo
'
);
expect
(
axios
.
get
).
toHaveBeenCalledWith
(
mockProps
.
projectsFetchPath
,
expect
.
objectContaining
({
params
:
{
search
:
'
foo
'
,
},
}),
);
});
it
(
'
sets response to `projects` and focuses on searchInput when request is successful
'
,
async
()
=>
{
jest
.
spyOn
(
axios
,
'
get
'
).
mockResolvedValue
({
data
:
mockProjects
,
});
await
wrapper
.
vm
.
fetchProjects
(
'
foo
'
);
expect
(
wrapper
.
vm
.
projects
).
toBe
(
mockProjects
);
expect
(
wrapper
.
vm
.
$refs
.
searchInput
.
focusInput
).
toHaveBeenCalled
();
});
it
(
'
sets projectsListLoadFailed to true when request fails
'
,
async
()
=>
{
jest
.
spyOn
(
axios
,
'
get
'
).
mockRejectedValue
({});
await
wrapper
.
vm
.
fetchProjects
(
'
foo
'
);
expect
(
wrapper
.
vm
.
projectsListLoadFailed
).
toBe
(
true
);
});
it
(
'
sets projectsListLoading to false when request completes
'
,
async
()
=>
{
jest
.
spyOn
(
axios
,
'
get
'
).
mockResolvedValue
({
data
:
mockProjects
,
});
await
wrapper
.
vm
.
fetchProjects
(
'
foo
'
);
expect
(
wrapper
.
vm
.
projectsListLoading
).
toBe
(
false
);
});
});
describe
(
'
isSelectedProject
'
,
()
=>
{
it
.
each
`
project | selectedProject | title | returnValue
${
mockProjects
[
0
]}
|
${
mockProjects
[
0
]}
|
${
'
are same projects
'
}
|
${
true
}
${
mockProjects
[
0
]}
|
${
mockProjects
[
1
]}
|
${
'
are different projects
'
}
|
${
false
}
`
(
'
returns $returnValue when selectedProject and provided project param $title
'
,
async
({
project
,
selectedProject
,
returnValue
})
=>
{
wrapper
.
setData
({
selectedProject
,
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
isSelectedProject
(
project
)).
toBe
(
returnValue
);
},
);
it
(
'
returns false when selectedProject is null
'
,
async
()
=>
{
wrapper
.
setData
({
selectedProject
:
null
,
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
vm
.
isSelectedProject
(
mockProjects
[
0
])).
toBe
(
false
);
});
});
});
describe
(
'
template
'
,
()
=>
{
const
findDropdownEl
=
()
=>
wrapper
.
find
(
GlDropdown
);
it
(
'
renders collapsed state element with icon
'
,
()
=>
{
const
collapsedEl
=
wrapper
.
find
(
'
[data-testid="move-collapsed"]
'
);
expect
(
collapsedEl
.
exists
()).
toBe
(
true
);
expect
(
collapsedEl
.
attributes
(
'
title
'
)).
toBe
(
mockProps
.
dropdownButtonTitle
);
expect
(
collapsedEl
.
find
(
GlIcon
).
exists
()).
toBe
(
true
);
expect
(
collapsedEl
.
find
(
GlIcon
).
props
(
'
name
'
)).
toBe
(
'
arrow-right
'
);
});
describe
(
'
gl-dropdown component
'
,
()
=>
{
it
(
'
renders component container element
'
,
()
=>
{
expect
(
findDropdownEl
().
exists
()).
toBe
(
true
);
expect
(
findDropdownEl
().
props
(
'
block
'
)).
toBe
(
true
);
});
it
(
'
renders gl-dropdown-form component
'
,
()
=>
{
expect
(
findDropdownEl
()
.
find
(
GlDropdownForm
)
.
exists
(),
).
toBe
(
true
);
});
it
(
'
renders header element
'
,
()
=>
{
const
headerEl
=
findDropdownEl
().
find
(
'
[data-testid="header"]
'
);
expect
(
headerEl
.
exists
()).
toBe
(
true
);
expect
(
headerEl
.
find
(
'
span
'
).
text
()).
toBe
(
mockProps
.
dropdownHeaderTitle
);
expect
(
headerEl
.
find
(
GlButton
).
props
(
'
icon
'
)).
toBe
(
'
close
'
);
});
it
(
'
renders gl-search-box-by-type component
'
,
()
=>
{
const
searchEl
=
findDropdownEl
().
find
(
GlSearchBoxByType
);
expect
(
searchEl
.
exists
()).
toBe
(
true
);
expect
(
searchEl
.
attributes
()).
toMatchObject
({
placeholder
:
'
Search project
'
,
debounce
:
'
300
'
,
});
});
it
(
'
renders gl-loading-icon component when projectsListLoading prop is true
'
,
async
()
=>
{
wrapper
.
setData
({
projectsListLoading
:
true
,
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
findDropdownEl
()
.
find
(
GlLoadingIcon
)
.
exists
(),
).
toBe
(
true
);
});
it
(
'
renders gl-dropdown-item components for available projects
'
,
async
()
=>
{
wrapper
.
setData
({
projects
:
mockProjects
,
selectedProject
:
mockProjects
[
0
],
});
await
wrapper
.
vm
.
$nextTick
();
const
dropdownItems
=
wrapper
.
findAll
(
GlDropdownItem
);
expect
(
dropdownItems
).
toHaveLength
(
mockProjects
.
length
);
expect
(
dropdownItems
.
at
(
0
).
props
()).
toMatchObject
({
isCheckItem
:
true
,
isChecked
:
true
,
});
expect
(
dropdownItems
.
at
(
0
).
text
()).
toBe
(
mockProjects
[
0
].
name_with_namespace
);
});
it
(
'
renders string "No matching results" when search does not yield any matches
'
,
async
()
=>
{
wrapper
.
setData
({
searchKey
:
'
foo
'
,
});
// Wait for `searchKey` watcher to run.
await
wrapper
.
vm
.
$nextTick
();
wrapper
.
setData
({
projects
:
[],
projectsListLoading
:
false
,
});
await
wrapper
.
vm
.
$nextTick
();
const
dropdownContentEl
=
wrapper
.
find
(
'
[data-testid="content"]
'
);
expect
(
dropdownContentEl
.
text
()).
toContain
(
'
No matching results
'
);
});
it
(
'
renders string "Failed to load projects" when loading projects list fails
'
,
async
()
=>
{
wrapper
.
setData
({
projects
:
[],
projectsListLoading
:
false
,
projectsListLoadFailed
:
true
,
});
await
wrapper
.
vm
.
$nextTick
();
const
dropdownContentEl
=
wrapper
.
find
(
'
[data-testid="content"]
'
);
expect
(
dropdownContentEl
.
text
()).
toContain
(
'
Failed to load projects
'
);
});
it
(
'
renders gl-button within footer
'
,
async
()
=>
{
const
moveButtonEl
=
wrapper
.
find
(
'
[data-testid="footer"]
'
).
find
(
GlButton
);
expect
(
moveButtonEl
.
text
()).
toBe
(
'
Move
'
);
expect
(
moveButtonEl
.
attributes
(
'
disabled
'
)).
toBe
(
'
true
'
);
wrapper
.
setData
({
selectedProject
:
mockProjects
[
0
],
});
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
find
(
'
[data-testid="footer"]
'
)
.
find
(
GlButton
)
.
attributes
(
'
disabled
'
),
).
not
.
toBeDefined
();
});
});
describe
(
'
events
'
,
()
=>
{
it
(
'
collapsed state element emits `toggle-collapse` event on component when clicked
'
,
()
=>
{
wrapper
.
find
(
'
[data-testid="move-collapsed"]
'
).
trigger
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
toggle-collapse
'
)).
toBeTruthy
();
});
it
(
'
gl-dropdown component calls `fetchProjects` on `shown` event
'
,
()
=>
{
jest
.
spyOn
(
axios
,
'
get
'
).
mockResolvedValue
({
data
:
mockProjects
,
});
findDropdownEl
().
vm
.
$emit
(
'
shown
'
);
expect
(
axios
.
get
).
toHaveBeenCalled
();
});
it
(
'
gl-dropdown component prevents dropdown body from closing on `hide` event when `projectItemClick` prop is true
'
,
async
()
=>
{
wrapper
.
setData
({
projectItemClick
:
true
,
});
findDropdownEl
().
vm
.
$emit
(
'
hide
'
,
mockEvent
);
expect
(
mockEvent
.
preventDefault
).
toHaveBeenCalled
();
expect
(
wrapper
.
vm
.
projectItemClick
).
toBe
(
false
);
});
it
(
'
gl-dropdown component emits `dropdown-close` event on component from `hide` event
'
,
async
()
=>
{
findDropdownEl
().
vm
.
$emit
(
'
hide
'
);
expect
(
wrapper
.
emitted
(
'
dropdown-close
'
)).
toBeTruthy
();
});
it
(
'
close icon in dropdown header closes the dropdown when clicked
'
,
()
=>
{
wrapper
.
find
(
'
[data-testid="header"]
'
)
.
find
(
GlButton
)
.
vm
.
$emit
(
'
click
'
,
mockEvent
);
expect
(
wrapper
.
vm
.
$refs
.
dropdown
.
hide
).
toHaveBeenCalled
();
});
it
(
'
sets project for clicked gl-dropdown-item to selectedProject
'
,
async
()
=>
{
wrapper
.
setData
({
projects
:
mockProjects
,
});
await
wrapper
.
vm
.
$nextTick
();
wrapper
.
findAll
(
GlDropdownItem
)
.
at
(
0
)
.
vm
.
$emit
(
'
click
'
,
mockEvent
);
expect
(
wrapper
.
vm
.
selectedProject
).
toBe
(
mockProjects
[
0
]);
});
it
(
'
hides dropdown and emits `move-issuable` event when move button is clicked
'
,
async
()
=>
{
wrapper
.
setData
({
selectedProject
:
mockProjects
[
0
],
});
await
wrapper
.
vm
.
$nextTick
();
wrapper
.
find
(
'
[data-testid="footer"]
'
)
.
find
(
GlButton
)
.
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
$refs
.
dropdown
.
hide
).
toHaveBeenCalled
();
expect
(
wrapper
.
emitted
(
'
move-issuable
'
)).
toBeTruthy
();
expect
(
wrapper
.
emitted
(
'
move-issuable
'
)[
0
]).
toEqual
([
mockProjects
[
0
]]);
});
});
});
});
spec/services/projects/container_repository/delete_tags_service_spec.rb
View file @
9ba1176e
...
...
@@ -27,13 +27,17 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do
end
end
RSpec
.
shared_examples
'logging an error response'
do
|
message:
'could not delete tags'
|
RSpec
.
shared_examples
'logging an error response'
do
|
message:
'could not delete tags'
,
extra_log:
{}
|
it
'logs an error message'
do
expect
(
service
).
to
receive
(
:log_error
).
with
(
log_data
=
{
service_class:
'Projects::ContainerRepository::DeleteTagsService'
,
message:
message
,
container_repository_id:
repository
.
id
)
}
log_data
.
merge!
(
extra_log
)
if
extra_log
.
any?
expect
(
service
).
to
receive
(
:log_error
).
with
(
log_data
)
subject
end
...
...
@@ -115,7 +119,7 @@ RSpec.describe Projects::ContainerRepository::DeleteTagsService do
it
{
is_expected
.
to
include
(
status: :error
,
message:
'timeout while deleting tags'
)
}
it_behaves_like
'logging an error response'
,
message:
'timeout while deleting tags'
it_behaves_like
'logging an error response'
,
message:
'timeout while deleting tags'
,
extra_log:
{
deleted_tags_count:
0
}
end
end
end
...
...
spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb
View file @
9ba1176e
...
...
@@ -67,7 +67,7 @@ RSpec.describe Projects::ContainerRepository::Gitlab::DeleteTagsService do
stub_delete_reference_requests
(
'A'
=>
200
)
end
it
{
is_expected
.
to
include
(
status: :error
,
message:
'timeout while deleting tags'
)
}
it
{
is_expected
.
to
eq
(
status: :error
,
message:
'timeout while deleting tags'
,
deleted:
[
'A'
]
)
}
it
'tracks the exception'
do
expect
(
::
Gitlab
::
ErrorTracking
)
...
...
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