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
6f3b0814
Commit
6f3b0814
authored
Mar 09, 2021
by
Nicolò Maria Mezzopera
Committed by
Mike Greiling
Mar 09, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Set breadcrumb to Root image when name missing
- source - tests
parent
5492f611
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
138 additions
and
33 deletions
+138
-33
app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
...istry/explorer/components/details_page/details_header.vue
+21
-11
app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
...registry/explorer/components/list_page/image_list_row.vue
+5
-1
app/assets/javascripts/registry/explorer/constants/common.js
app/assets/javascripts/registry/explorer/constants/common.js
+3
-0
app/assets/javascripts/registry/explorer/constants/details.js
...assets/javascripts/registry/explorer/constants/details.js
+6
-2
app/assets/javascripts/registry/explorer/constants/index.js
app/assets/javascripts/registry/explorer/constants/index.js
+1
-0
app/assets/javascripts/registry/explorer/pages/details.vue
app/assets/javascripts/registry/explorer/pages/details.vue
+5
-2
changelogs/unreleased/320901-container-page-says-image-repository-not-found.yml
...320901-container-page-says-image-repository-not-found.yml
+5
-0
locale/gitlab.pot
locale/gitlab.pot
+6
-3
spec/features/groups/container_registry_spec.rb
spec/features/groups/container_registry_spec.rb
+7
-1
spec/features/projects/container_registry_spec.rb
spec/features/projects/container_registry_spec.rb
+7
-1
spec/frontend/registry/explorer/components/details_page/details_header_spec.js
...y/explorer/components/details_page/details_header_spec.js
+41
-10
spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js
...stry/explorer/components/list_page/image_list_row_spec.js
+9
-2
spec/frontend/registry/explorer/pages/details_spec.js
spec/frontend/registry/explorer/pages/details_spec.js
+22
-0
No files found.
app/assets/javascripts/registry/explorer/components/details_page/details_header.vue
View file @
6f3b0814
<
script
>
import
{
Gl
Sprintf
,
GlButton
}
from
'
@gitlab/ui
'
;
import
{
Gl
Button
,
GlIcon
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
{
sprintf
,
n__
}
from
'
~/locale
'
;
import
MetadataItem
from
'
~/vue_shared/components/registry/metadata_item.vue
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
import
{
DETAILS_PAGE_TITLE
,
UPDATED_AT
,
CLEANUP_UNSCHEDULED_TEXT
,
CLEANUP_SCHEDULED_TEXT
,
...
...
@@ -20,11 +19,16 @@ import {
UNSCHEDULED_STATUS
,
SCHEDULED_STATUS
,
ONGOING_STATUS
,
ROOT_IMAGE_TEXT
,
ROOT_IMAGE_TOOLTIP
,
}
from
'
../../constants/index
'
;
export
default
{
name
:
'
DetailsHeader
'
,
components
:
{
GlSprintf
,
GlButton
,
TitleArea
,
MetadataItem
},
components
:
{
GlButton
,
GlIcon
,
TitleArea
,
MetadataItem
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
mixins
:
[
timeagoMixin
],
props
:
{
image
:
{
...
...
@@ -73,9 +77,12 @@ export default {
deleteButtonDisabled
()
{
return
this
.
disabled
||
!
this
.
image
.
canDelete
;
},
},
i18n
:
{
DETAILS_PAGE_TITLE
,
rootImageTooltip
()
{
return
!
this
.
image
.
name
?
ROOT_IMAGE_TOOLTIP
:
''
;
},
imageName
()
{
return
this
.
image
.
name
||
ROOT_IMAGE_TEXT
;
},
},
};
</
script
>
...
...
@@ -84,12 +91,15 @@ export default {
<title-area
:metadata-loading=
"metadataLoading"
>
<template
#title
>
<span
data-testid=
"title"
>
<gl-sprintf
:message=
"$options.i18n.DETAILS_PAGE_TITLE"
>
<template
#imageName
>
{{
image
.
name
}}
</
template
>
</gl-sprintf>
{{
imageName
}}
</span>
<gl-icon
v-if=
"rootImageTooltip"
v-gl-tooltip=
"rootImageTooltip"
class=
"gl-text-blue-600"
name=
"information-o"
:aria-label=
"rootImageTooltip"
/>
</
template
>
<
template
#metadata-tags-count
>
<metadata-item
icon=
"tag"
:text=
"tagCountText"
data-testid=
"tags-count"
/>
...
...
app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
View file @
6f3b0814
...
...
@@ -13,6 +13,7 @@ import {
CLEANUP_TIMED_OUT_ERROR_MESSAGE
,
IMAGE_DELETE_SCHEDULED_STATUS
,
IMAGE_FAILED_DELETED_STATUS
,
ROOT_IMAGE_TEXT
,
}
from
'
../../constants/index
'
;
import
DeleteButton
from
'
../delete_button.vue
'
;
...
...
@@ -74,6 +75,9 @@ export default {
}
return
null
;
},
imageName
()
{
return
this
.
item
.
name
?
this
.
item
.
path
:
`
${
this
.
item
.
path
}
/
${
ROOT_IMAGE_TEXT
}
`
;
},
},
};
</
script
>
...
...
@@ -95,7 +99,7 @@ export default {
data-qa-selector=
"registry_image_content"
:to=
"
{ name: 'details', params: { id } }"
>
{{
i
tem
.
path
}}
{{
i
mageName
}}
</router-link>
<clipboard-button
v-if=
"item.location"
...
...
app/assets/javascripts/registry/explorer/constants/common.js
0 → 100644
View file @
6f3b0814
import
{
s__
}
from
'
~/locale
'
;
export
const
ROOT_IMAGE_TEXT
=
s__
(
'
ContainerRegistry|Root image
'
);
app/assets/javascripts/registry/explorer/constants/details.js
View file @
6f3b0814
...
...
@@ -2,7 +2,6 @@ import { helpPagePath } from '~/helpers/help_page_helper';
import
{
s__
,
__
}
from
'
~/locale
'
;
// Translations strings
export
const
DETAILS_PAGE_TITLE
=
s__
(
'
ContainerRegistry|%{imageName} tags
'
);
export
const
DELETE_TAG_ERROR_MESSAGE
=
s__
(
'
ContainerRegistry|Something went wrong while marking the tag for deletion.
'
,
);
...
...
@@ -53,7 +52,8 @@ export const MISSING_OR_DELETED_IMAGE_TITLE = s__(
export
const
MISSING_OR_DELETED_IMAGE_MESSAGE
=
s__
(
'
ContainerRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page.
'
,
);
export
const
MISSING_OR_DELETE_IMAGE_BREADCRUMB
=
s__
(
export
const
MISSING_OR_DELETED_IMAGE_BREADCRUMB
=
s__
(
'
ContainerRegistry|Image repository not found
'
,
);
...
...
@@ -112,6 +112,10 @@ export const FAILED_DELETION_STATUS_MESSAGE = s__(
'
ContainerRegistry|This image repository has failed to be deleted
'
,
);
export
const
ROOT_IMAGE_TOOLTIP
=
s__
(
'
ContainerRegistry|Image repository with no name located at the project URL.
'
,
);
// Parameters
export
const
DEFAULT_PAGE
=
1
;
...
...
app/assets/javascripts/registry/explorer/constants/index.js
View file @
6f3b0814
export
*
from
'
./common
'
;
export
*
from
'
./expiration_policies
'
;
export
*
from
'
./quick_start
'
;
export
*
from
'
./list
'
;
...
...
app/assets/javascripts/registry/explorer/pages/details.vue
View file @
6f3b0814
...
...
@@ -24,7 +24,8 @@ import {
GRAPHQL_PAGE_SIZE
,
FETCH_IMAGES_LIST_ERROR_MESSAGE
,
UNFINISHED_STATUS
,
MISSING_OR_DELETE_IMAGE_BREADCRUMB
,
MISSING_OR_DELETED_IMAGE_BREADCRUMB
,
ROOT_IMAGE_TEXT
,
}
from
'
../constants/index
'
;
import
deleteContainerRepositoryTagsMutation
from
'
../graphql/mutations/delete_container_repository_tags.mutation.graphql
'
;
import
getContainerRepositoryDetailsQuery
from
'
../graphql/queries/get_container_repository_details.query.graphql
'
;
...
...
@@ -116,7 +117,9 @@ export default {
},
methods
:
{
updateBreadcrumb
()
{
const
name
=
this
.
image
?.
name
||
MISSING_OR_DELETE_IMAGE_BREADCRUMB
;
const
name
=
this
.
image
?.
id
?
this
.
image
?.
name
||
ROOT_IMAGE_TEXT
:
MISSING_OR_DELETED_IMAGE_BREADCRUMB
;
this
.
breadCrumbState
.
updateName
(
name
);
},
deleteTags
(
toBeDeleted
)
{
...
...
changelogs/unreleased/320901-container-page-says-image-repository-not-found.yml
0 → 100644
View file @
6f3b0814
---
title
:
Use Root Image for images with missing name
merge_request
:
54693
author
:
type
:
changed
locale/gitlab.pot
View file @
6f3b0814
...
...
@@ -7958,9 +7958,6 @@ msgid_plural "ContainerRegistry|%{count} Tags"
msgstr[0] ""
msgstr[1] ""
msgid "ContainerRegistry|%{imageName} tags"
msgstr ""
msgid "ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted."
msgstr ""
...
...
@@ -8063,6 +8060,9 @@ msgstr ""
msgid "ContainerRegistry|Image repository will be deleted"
msgstr ""
msgid "ContainerRegistry|Image repository with no name located at the project URL."
msgstr ""
msgid "ContainerRegistry|Image tags"
msgstr ""
...
...
@@ -8128,6 +8128,9 @@ msgstr ""
msgid "ContainerRegistry|Remove these tags"
msgstr ""
msgid "ContainerRegistry|Root image"
msgstr ""
msgid "ContainerRegistry|Run cleanup:"
msgstr ""
...
...
spec/features/groups/container_registry_spec.rb
View file @
6f3b0814
...
...
@@ -67,7 +67,13 @@ RSpec.describe 'Container Registry', :js do
end
it
'shows the image title'
do
expect
(
page
).
to
have_content
'my/image tags'
expect
(
page
).
to
have_content
'my/image'
end
it
'shows the image tags'
do
expect
(
page
).
to
have_content
'Image tags'
first_tag
=
first
(
'[data-testid="name"]'
)
expect
(
first_tag
).
to
have_content
'latest'
end
it
'user removes a specific tag from container repository'
do
...
...
spec/features/projects/container_registry_spec.rb
View file @
6f3b0814
...
...
@@ -82,7 +82,13 @@ RSpec.describe 'Container Registry', :js do
end
it
'shows the image title'
do
expect
(
page
).
to
have_content
'my/image tags'
expect
(
page
).
to
have_content
'my/image'
end
it
'shows the image tags'
do
expect
(
page
).
to
have_content
'Image tags'
first_tag
=
first
(
'[data-testid="name"]'
)
expect
(
first_tag
).
to
have_content
'1'
end
it
'user removes a specific tag from container repository'
do
...
...
spec/frontend/registry/explorer/components/details_page/details_header_spec.js
View file @
6f3b0814
import
{
Gl
Sprintf
,
GlButt
on
}
from
'
@gitlab/ui
'
;
import
{
Gl
Button
,
GlIc
on
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
useFakeDate
}
from
'
helpers/fake_date
'
;
import
{
createMockDirective
,
getBinding
}
from
'
helpers/vue_mock_directive
'
;
import
component
from
'
~/registry/explorer/components/details_page/details_header.vue
'
;
import
{
DETAILS_PAGE_TITLE
,
UNSCHEDULED_STATUS
,
SCHEDULED_STATUS
,
ONGOING_STATUS
,
...
...
@@ -13,6 +13,8 @@ import {
CLEANUP_SCHEDULED_TOOLTIP
,
CLEANUP_ONGOING_TOOLTIP
,
CLEANUP_UNFINISHED_TOOLTIP
,
ROOT_IMAGE_TEXT
,
ROOT_IMAGE_TOOLTIP
,
}
from
'
~/registry/explorer/constants
'
;
import
TitleArea
from
'
~/vue_shared/components/registry/title_area.vue
'
;
...
...
@@ -41,6 +43,7 @@ describe('Details Header', () => {
const
findTagsCount
=
()
=>
findByTestId
(
'
tags-count
'
);
const
findCleanup
=
()
=>
findByTestId
(
'
cleanup
'
);
const
findDeleteButton
=
()
=>
wrapper
.
find
(
GlButton
);
const
findInfoIcon
=
()
=>
wrapper
.
find
(
GlIcon
);
const
waitForMetadataItems
=
async
()
=>
{
// Metadata items are printed by a loop in the title-area and it takes two ticks for them to be available
...
...
@@ -51,8 +54,10 @@ describe('Details Header', () => {
const
mountComponent
=
(
propsData
=
{
image
:
defaultImage
})
=>
{
wrapper
=
shallowMount
(
component
,
{
propsData
,
directives
:
{
GlTooltip
:
createMockDirective
(),
},
stubs
:
{
GlSprintf
,
TitleArea
,
},
});
...
...
@@ -62,15 +67,41 @@ describe('Details Header', () => {
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
image name
'
,
()
=>
{
describe
(
'
missing image name
'
,
()
=>
{
it
(
'
root image
'
,
()
=>
{
mountComponent
({
image
:
{
...
defaultImage
,
name
:
''
}
});
it
(
'
has the correct title
'
,
()
=>
{
mountComponent
({
image
:
{
...
defaultImage
,
name
:
''
}
});
expect
(
findTitle
().
text
()).
toMatchInterpolatedText
(
DETAILS_PAGE_TITLE
);
});
expect
(
findTitle
().
text
()).
toBe
(
ROOT_IMAGE_TEXT
);
});
it
(
'
shows imageName in the title
'
,
()
=>
{
mountComponent
();
expect
(
findTitle
().
text
()).
toContain
(
'
foo
'
);
it
(
'
has an icon
'
,
()
=>
{
mountComponent
({
image
:
{
...
defaultImage
,
name
:
''
}
});
expect
(
findInfoIcon
().
exists
()).
toBe
(
true
);
expect
(
findInfoIcon
().
props
(
'
name
'
)).
toBe
(
'
information-o
'
);
});
it
(
'
has a tooltip
'
,
()
=>
{
mountComponent
({
image
:
{
...
defaultImage
,
name
:
''
}
});
const
tooltip
=
getBinding
(
findInfoIcon
().
element
,
'
gl-tooltip
'
);
expect
(
tooltip
.
value
).
toBe
(
ROOT_IMAGE_TOOLTIP
);
});
});
describe
(
'
with image name present
'
,
()
=>
{
it
(
'
shows image.name
'
,
()
=>
{
mountComponent
();
expect
(
findTitle
().
text
()).
toContain
(
'
foo
'
);
});
it
(
'
has no icon
'
,
()
=>
{
mountComponent
();
expect
(
findInfoIcon
().
exists
()).
toBe
(
false
);
});
});
});
describe
(
'
delete button
'
,
()
=>
{
...
...
spec/frontend/registry/explorer/components/list_page/image_list_row_spec.js
View file @
6f3b0814
...
...
@@ -12,6 +12,7 @@ import {
CLEANUP_TIMED_OUT_ERROR_MESSAGE
,
IMAGE_DELETE_SCHEDULED_STATUS
,
IMAGE_FAILED_DELETED_STATUS
,
ROOT_IMAGE_TEXT
,
}
from
'
~/registry/explorer/constants
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
ListItem
from
'
~/vue_shared/components/registry/list_item.vue
'
;
...
...
@@ -73,8 +74,8 @@ describe('Image List Row', () => {
mountComponent
();
const
link
=
findDetailsLink
();
expect
(
link
.
html
()).
toContain
(
item
.
path
);
expect
(
link
.
props
(
'
to
'
)).
toMatchObject
({
expect
(
link
.
text
()).
toBe
(
item
.
path
);
expect
(
findDetailsLink
()
.
props
(
'
to
'
)).
toMatchObject
({
name
:
'
details
'
,
params
:
{
id
:
getIdFromGraphQLId
(
item
.
id
),
...
...
@@ -82,6 +83,12 @@ describe('Image List Row', () => {
});
});
it
(
`when the image has no name appends
${
ROOT_IMAGE_TEXT
}
to the path`
,
()
=>
{
mountComponent
({
item
:
{
...
item
,
name
:
''
}
});
expect
(
findDetailsLink
().
text
()).
toBe
(
`
${
item
.
path
}
/
${
ROOT_IMAGE_TEXT
}
`
);
});
it
(
'
contains a clipboard button
'
,
()
=>
{
mountComponent
();
const
button
=
findClipboardButton
();
...
...
spec/frontend/registry/explorer/pages/details_spec.js
View file @
6f3b0814
...
...
@@ -17,6 +17,8 @@ import {
UNFINISHED_STATUS
,
DELETE_SCHEDULED
,
ALERT_DANGER_IMAGE
,
MISSING_OR_DELETED_IMAGE_BREADCRUMB
,
ROOT_IMAGE_TEXT
,
}
from
'
~/registry/explorer/constants
'
;
import
deleteContainerRepositoryTagsMutation
from
'
~/registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql
'
;
import
getContainerRepositoryDetailsQuery
from
'
~/registry/explorer/graphql/queries/get_container_repository_details.query.graphql
'
;
...
...
@@ -515,6 +517,26 @@ describe('Details Page', () => {
expect
(
breadCrumbState
.
updateName
).
toHaveBeenCalledWith
(
containerRepositoryMock
.
name
);
});
it
(
`when the image is missing set the breadcrumb to
${
MISSING_OR_DELETED_IMAGE_BREADCRUMB
}
`
,
async
()
=>
{
mountComponent
({
resolver
:
jest
.
fn
().
mockResolvedValue
(
graphQLEmptyImageDetailsMock
)
});
await
waitForApolloRequestRender
();
expect
(
breadCrumbState
.
updateName
).
toHaveBeenCalledWith
(
MISSING_OR_DELETED_IMAGE_BREADCRUMB
);
});
it
(
`when the image has no name set the breadcrumb to
${
ROOT_IMAGE_TEXT
}
`
,
async
()
=>
{
mountComponent
({
resolver
:
jest
.
fn
()
.
mockResolvedValue
(
graphQLImageDetailsMock
({
...
containerRepositoryMock
,
name
:
null
})),
});
await
waitForApolloRequestRender
();
expect
(
breadCrumbState
.
updateName
).
toHaveBeenCalledWith
(
ROOT_IMAGE_TEXT
);
});
});
describe
(
'
when the image has a status different from null
'
,
()
=>
{
...
...
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