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
c8a07385
Commit
c8a07385
authored
Jun 10, 2020
by
Nicolò Maria Mezzopera
Committed by
Mark Florian
Jun 10, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add tags loader component
- new component - unit tests - snapshots
parent
e28e7298
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
843 additions
and
477 deletions
+843
-477
app/assets/javascripts/registry/explorer/components/details_page/empty_tags_state.vue
...try/explorer/components/details_page/empty_tags_state.vue
+33
-0
app/assets/javascripts/registry/explorer/components/details_page/tags_loader.vue
...registry/explorer/components/details_page/tags_loader.vue
+34
-0
app/assets/javascripts/registry/explorer/components/details_page/tags_table.vue
.../registry/explorer/components/details_page/tags_table.vue
+210
-0
app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
...registry/explorer/components/list_page/image_list_row.vue
+1
-1
app/assets/javascripts/registry/explorer/pages/details.vue
app/assets/javascripts/registry/explorer/pages/details.vue
+19
-224
app/assets/javascripts/registry/explorer/stores/getters.js
app/assets/javascripts/registry/explorer/stores/getters.js
+0
-6
spec/features/groups/container_registry_spec.rb
spec/features/groups/container_registry_spec.rb
+1
-1
spec/features/projects/container_registry_spec.rb
spec/features/projects/container_registry_spec.rb
+1
-1
spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
...nents/details_page/__snapshots__/tags_loader_spec.js.snap
+63
-0
spec/frontend/registry/explorer/components/details_page/delete_alert_spec.js
...try/explorer/components/details_page/delete_alert_spec.js
+5
-0
spec/frontend/registry/explorer/components/details_page/delete_modal_spec.js
...try/explorer/components/details_page/delete_modal_spec.js
+5
-0
spec/frontend/registry/explorer/components/details_page/details_header_spec.js
...y/explorer/components/details_page/details_header_spec.js
+5
-0
spec/frontend/registry/explorer/components/details_page/empty_tags_state.js
...stry/explorer/components/details_page/empty_tags_state.js
+43
-0
spec/frontend/registry/explorer/components/details_page/tags_loader_spec.js
...stry/explorer/components/details_page/tags_loader_spec.js
+49
-0
spec/frontend/registry/explorer/components/details_page/tags_table_spec.js
...istry/explorer/components/details_page/tags_table_spec.js
+287
-0
spec/frontend/registry/explorer/components/list_page/image_list_spec.js
...registry/explorer/components/list_page/image_list_spec.js
+5
-0
spec/frontend/registry/explorer/pages/details_spec.js
spec/frontend/registry/explorer/pages/details_spec.js
+61
-215
spec/frontend/registry/explorer/stores/getters_spec.js
spec/frontend/registry/explorer/stores/getters_spec.js
+0
-29
spec/frontend/registry/explorer/stubs.js
spec/frontend/registry/explorer/stubs.js
+21
-0
No files found.
app/assets/javascripts/registry/explorer/components/details_page/empty_tags_state.vue
0 → 100644
View file @
c8a07385
<
script
>
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
{
EMPTY_IMAGE_REPOSITORY_TITLE
,
EMPTY_IMAGE_REPOSITORY_MESSAGE
,
}
from
'
../../constants/index
'
;
export
default
{
components
:
{
GlEmptyState
,
},
props
:
{
noContainersImage
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
i18n
:
{
EMPTY_IMAGE_REPOSITORY_TITLE
,
EMPTY_IMAGE_REPOSITORY_MESSAGE
,
},
};
</
script
>
<
template
>
<gl-empty-state
:title=
"$options.i18n.EMPTY_IMAGE_REPOSITORY_TITLE"
:svg-path=
"noContainersImage"
:description=
"$options.i18n.EMPTY_IMAGE_REPOSITORY_MESSAGE"
class=
"gl-mx-auto gl-my-0"
/>
</
template
>
app/assets/javascripts/registry/explorer/components/details_page/tags_loader.vue
0 → 100644
View file @
c8a07385
<
script
>
import
{
GlSkeletonLoader
}
from
'
@gitlab/ui
'
;
export
default
{
components
:
{
GlSkeletonLoader
,
},
loader
:
{
repeat
:
10
,
width
:
1000
,
height
:
40
,
},
};
</
script
>
<
template
>
<div>
<gl-skeleton-loader
v-for=
"index in $options.loader.repeat"
:key=
"index"
:width=
"$options.loader.width"
:height=
"$options.loader.height"
preserve-aspect-ratio=
"xMinYMax meet"
>
<rect
width=
"15"
x=
"0"
y=
"12.5"
height=
"15"
rx=
"4"
/>
<rect
width=
"250"
x=
"25"
y=
"10"
height=
"20"
rx=
"4"
/>
<circle
cx=
"290"
cy=
"20"
r=
"10"
/>
<rect
width=
"100"
x=
"315"
y=
"10"
height=
"20"
rx=
"4"
/>
<rect
width=
"100"
x=
"500"
y=
"10"
height=
"20"
rx=
"4"
/>
<rect
width=
"100"
x=
"630"
y=
"10"
height=
"20"
rx=
"4"
/>
<rect
x=
"960"
y=
"0"
width=
"40"
height=
"40"
rx=
"4"
/>
</gl-skeleton-loader>
</div>
</
template
>
app/assets/javascripts/registry/explorer/components/details_page/tags_table.vue
0 → 100644
View file @
c8a07385
<
script
>
import
{
GlTable
,
GlFormCheckbox
,
GlButton
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
{
n__
}
from
'
~/locale
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
{
numberToHumanSize
}
from
'
~/lib/utils/number_utils
'
;
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
import
{
LIST_KEY_TAG
,
LIST_KEY_IMAGE_ID
,
LIST_KEY_SIZE
,
LIST_KEY_LAST_UPDATED
,
LIST_KEY_ACTIONS
,
LIST_KEY_CHECKBOX
,
LIST_LABEL_TAG
,
LIST_LABEL_IMAGE_ID
,
LIST_LABEL_SIZE
,
LIST_LABEL_LAST_UPDATED
,
REMOVE_TAGS_BUTTON_TITLE
,
REMOVE_TAG_BUTTON_TITLE
,
}
from
'
../../constants/index
'
;
export
default
{
components
:
{
GlTable
,
GlFormCheckbox
,
GlButton
,
ClipboardButton
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
mixins
:
[
timeagoMixin
],
props
:
{
tags
:
{
type
:
Array
,
required
:
false
,
default
:
()
=>
[],
},
isLoading
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
isDesktop
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
i18n
:
{
REMOVE_TAGS_BUTTON_TITLE
,
REMOVE_TAG_BUTTON_TITLE
,
},
data
()
{
return
{
selectedItems
:
[],
};
},
computed
:
{
fields
()
{
const
tagClass
=
this
.
isDesktop
?
'
w-25
'
:
''
;
const
tagInnerClass
=
this
.
isDesktop
?
'
mw-m
'
:
'
gl-justify-content-end
'
;
return
[
{
key
:
LIST_KEY_CHECKBOX
,
label
:
''
,
class
:
'
gl-w-16
'
},
{
key
:
LIST_KEY_TAG
,
label
:
LIST_LABEL_TAG
,
class
:
`
${
tagClass
}
js-tag-column`
,
innerClass
:
tagInnerClass
,
},
{
key
:
LIST_KEY_IMAGE_ID
,
label
:
LIST_LABEL_IMAGE_ID
},
{
key
:
LIST_KEY_SIZE
,
label
:
LIST_LABEL_SIZE
},
{
key
:
LIST_KEY_LAST_UPDATED
,
label
:
LIST_LABEL_LAST_UPDATED
},
{
key
:
LIST_KEY_ACTIONS
,
label
:
''
},
].
filter
(
f
=>
f
.
key
!==
LIST_KEY_CHECKBOX
||
this
.
isDesktop
);
},
tagsNames
()
{
return
this
.
tags
.
map
(
t
=>
t
.
name
);
},
selectAllChecked
()
{
return
this
.
selectedItems
.
length
===
this
.
tags
.
length
&&
this
.
tags
.
length
>
0
;
},
},
watch
:
{
tagsNames
:
{
immediate
:
false
,
handler
(
tagsNames
)
{
this
.
selectedItems
=
this
.
selectedItems
.
filter
(
t
=>
tagsNames
.
includes
(
t
));
},
},
},
methods
:
{
formatSize
(
size
)
{
return
numberToHumanSize
(
size
);
},
layers
(
layers
)
{
return
layers
?
n__
(
'
%d layer
'
,
'
%d layers
'
,
layers
)
:
''
;
},
onSelectAllChange
()
{
if
(
this
.
selectAllChecked
)
{
this
.
selectedItems
=
[];
}
else
{
this
.
selectedItems
=
this
.
tags
.
map
(
x
=>
x
.
name
);
}
},
updateSelectedItems
(
name
)
{
const
delIndex
=
this
.
selectedItems
.
findIndex
(
x
=>
x
===
name
);
if
(
delIndex
>
-
1
)
{
this
.
selectedItems
.
splice
(
delIndex
,
1
);
}
else
{
this
.
selectedItems
.
push
(
name
);
}
},
},
};
</
script
>
<
template
>
<gl-table
:items=
"tags"
:fields=
"fields"
:stacked=
"!isDesktop"
show-empty
:busy=
"isLoading"
>
<template
v-if=
"isDesktop"
#head
(
checkbox
)
>
<gl-form-checkbox
data-testid=
"mainCheckbox"
:checked=
"selectAllChecked"
@
change=
"onSelectAllChange"
/>
</
template
>
<
template
#head
(
actions
)
>
<span
class=
"gl-display-flex gl-justify-content-end"
>
<gl-button
v-gl-tooltip
data-testid=
"bulkDeleteButton"
:disabled=
"!selectedItems || selectedItems.length === 0"
icon=
"remove"
variant=
"danger"
:title=
"$options.i18n.REMOVE_TAGS_BUTTON_TITLE"
:aria-label=
"$options.i18n.REMOVE_TAGS_BUTTON_TITLE"
@
click=
"$emit('delete', selectedItems)"
/>
</span>
</
template
>
<
template
#cell(checkbox)=
"{item}"
>
<gl-form-checkbox
data-testid=
"rowCheckbox"
:checked=
"selectedItems.includes(item.name)"
@
change=
"updateSelectedItems(item.name)"
/>
</
template
>
<
template
#cell(name)=
"{item, field}"
>
<div
data-testid=
"rowName"
:class=
"[field.innerClass, 'gl-display-flex']"
>
<span
v-gl-tooltip
data-testid=
"rowNameText"
:title=
"item.name"
class=
"gl-text-overflow-ellipsis gl-overflow-hidden gl-white-space-nowrap"
>
{{
item
.
name
}}
</span>
<clipboard-button
v-if=
"item.location"
data-testid=
"rowClipboardButton"
:title=
"item.location"
:text=
"item.location"
css-class=
"btn-default btn-transparent btn-clipboard"
/>
</div>
</
template
>
<
template
#cell(short_revision)=
"{value}"
>
<span
data-testid=
"rowShortRevision"
>
{{
value
}}
</span>
</
template
>
<
template
#cell(total_size)=
"{item}"
>
<span
data-testid=
"rowSize"
>
{{
formatSize
(
item
.
total_size
)
}}
<template
v-if=
"item.total_size && item.layers"
>
·
</
template
>
{{ layers(item.layers) }}
</span>
</template>
<
template
#cell(created_at)=
"{value}"
>
<span
v-gl-tooltip
data-testid=
"rowTime"
:title=
"tooltipTitle(value)"
>
{{
timeFormatted
(
value
)
}}
</span>
</
template
>
<
template
#cell(actions)=
"{item}"
>
<span
class=
"gl-display-flex gl-justify-content-end"
>
<gl-button
data-testid=
"singleDeleteButton"
:title=
"$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:aria-label=
"$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:disabled=
"!item.destroy_path"
variant=
"danger"
icon=
"remove"
category=
"secondary"
@
click=
"$emit('delete', [item.name])"
/>
</span>
</
template
>
<
template
#empty
>
<slot
name=
"empty"
></slot>
</
template
>
<
template
#table-busy
>
<slot
name=
"loader"
></slot>
</
template
>
</gl-table>
</template>
app/assets/javascripts/registry/explorer/components/list_page/image_list_row.vue
View file @
c8a07385
...
@@ -125,7 +125,7 @@ export default {
...
@@ -125,7 +125,7 @@ export default {
:disabled=
"disabledDelete"
:disabled=
"disabledDelete"
:title=
"$options.i18n.REMOVE_REPOSITORY_LABEL"
:title=
"$options.i18n.REMOVE_REPOSITORY_LABEL"
:aria-label=
"$options.i18n.REMOVE_REPOSITORY_LABEL"
:aria-label=
"$options.i18n.REMOVE_REPOSITORY_LABEL"
c
lass=
"btn-inverted
"
c
ategory=
"secondary
"
variant=
"danger"
variant=
"danger"
icon=
"remove"
icon=
"remove"
@
click=
"$emit('delete', item)"
@
click=
"$emit('delete', item)"
...
...
app/assets/javascripts/registry/explorer/pages/details.vue
View file @
c8a07385
<
script
>
<
script
>
import
{
mapState
,
mapActions
,
mapGetters
}
from
'
vuex
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
import
{
GlPagination
,
GlResizeObserverDirective
}
from
'
@gitlab/ui
'
;
GlTable
,
GlFormCheckbox
,
GlDeprecatedButton
,
GlIcon
,
GlTooltipDirective
,
GlPagination
,
GlEmptyState
,
GlResizeObserverDirective
,
GlSkeletonLoader
,
}
from
'
@gitlab/ui
'
;
import
{
GlBreakpointInstance
}
from
'
@gitlab/ui/dist/utils
'
;
import
{
GlBreakpointInstance
}
from
'
@gitlab/ui/dist/utils
'
;
import
{
n__
}
from
'
~/locale
'
;
import
ClipboardButton
from
'
~/vue_shared/components/clipboard_button.vue
'
;
import
{
numberToHumanSize
}
from
'
~/lib/utils/number_utils
'
;
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
import
Tracking
from
'
~/tracking
'
;
import
Tracking
from
'
~/tracking
'
;
import
DeleteAlert
from
'
../components/details_page/delete_alert.vue
'
;
import
DeleteAlert
from
'
../components/details_page/delete_alert.vue
'
;
import
DeleteModal
from
'
../components/details_page/delete_modal.vue
'
;
import
DeleteModal
from
'
../components/details_page/delete_modal.vue
'
;
import
DetailsHeader
from
'
../components/details_page/details_header.vue
'
;
import
DetailsHeader
from
'
../components/details_page/details_header.vue
'
;
import
TagsTable
from
'
../components/details_page/tags_table.vue
'
;
import
TagsLoader
from
'
../components/details_page/tags_loader.vue
'
;
import
EmptyTagsState
from
'
../components/details_page/empty_tags_state.vue
'
;
import
{
decodeAndParse
}
from
'
../utils
'
;
import
{
decodeAndParse
}
from
'
../utils
'
;
import
{
import
{
LIST_KEY_TAG
,
LIST_KEY_IMAGE_ID
,
LIST_KEY_SIZE
,
LIST_KEY_LAST_UPDATED
,
LIST_KEY_ACTIONS
,
LIST_KEY_CHECKBOX
,
LIST_LABEL_TAG
,
LIST_LABEL_IMAGE_ID
,
LIST_LABEL_SIZE
,
LIST_LABEL_LAST_UPDATED
,
REMOVE_TAGS_BUTTON_TITLE
,
REMOVE_TAG_BUTTON_TITLE
,
EMPTY_IMAGE_REPOSITORY_TITLE
,
EMPTY_IMAGE_REPOSITORY_MESSAGE
,
ALERT_SUCCESS_TAG
,
ALERT_SUCCESS_TAG
,
ALERT_DANGER_TAG
,
ALERT_DANGER_TAG
,
ALERT_SUCCESS_TAGS
,
ALERT_SUCCESS_TAGS
,
...
@@ -46,66 +22,29 @@ export default {
...
@@ -46,66 +22,29 @@ export default {
components
:
{
components
:
{
DeleteAlert
,
DeleteAlert
,
DetailsHeader
,
DetailsHeader
,
GlTable
,
GlFormCheckbox
,
GlDeprecatedButton
,
GlIcon
,
ClipboardButton
,
GlPagination
,
GlPagination
,
DeleteModal
,
DeleteModal
,
GlSkeletonLoader
,
TagsTable
,
GlEmptyState
,
TagsLoader
,
EmptyTagsState
,
},
},
directives
:
{
directives
:
{
GlTooltip
:
GlTooltipDirective
,
GlResizeObserver
:
GlResizeObserverDirective
,
GlResizeObserver
:
GlResizeObserverDirective
,
},
},
mixins
:
[
timeagoMixin
,
Tracking
.
mixin
()],
mixins
:
[
Tracking
.
mixin
()],
loader
:
{
repeat
:
10
,
width
:
1000
,
height
:
40
,
},
i18n
:
{
REMOVE_TAGS_BUTTON_TITLE
,
REMOVE_TAG_BUTTON_TITLE
,
EMPTY_IMAGE_REPOSITORY_TITLE
,
EMPTY_IMAGE_REPOSITORY_MESSAGE
,
},
data
()
{
data
()
{
return
{
return
{
selectedItems
:
[],
itemsToBeDeleted
:
[],
itemsToBeDeleted
:
[],
selectAllChecked
:
false
,
modalDescription
:
null
,
isDesktop
:
true
,
isDesktop
:
true
,
deleteAlertType
:
null
,
deleteAlertType
:
null
,
};
};
},
},
computed
:
{
computed
:
{
...
mapGetters
([
'
tags
'
]),
...
mapState
([
'
tagsPagination
'
,
'
isLoading
'
,
'
config
'
,
'
tags
'
]),
...
mapState
([
'
tagsPagination
'
,
'
isLoading
'
,
'
config
'
]),
imageName
()
{
imageName
()
{
const
{
name
}
=
decodeAndParse
(
this
.
$route
.
params
.
id
);
const
{
name
}
=
decodeAndParse
(
this
.
$route
.
params
.
id
);
return
name
;
return
name
;
},
},
fields
()
{
const
tagClass
=
this
.
isDesktop
?
'
w-25
'
:
''
;
const
tagInnerClass
=
this
.
isDesktop
?
'
mw-m
'
:
'
gl-justify-content-end
'
;
return
[
{
key
:
LIST_KEY_CHECKBOX
,
label
:
''
,
class
:
'
gl-w-16
'
},
{
key
:
LIST_KEY_TAG
,
label
:
LIST_LABEL_TAG
,
class
:
`
${
tagClass
}
js-tag-column`
,
innerClass
:
tagInnerClass
,
},
{
key
:
LIST_KEY_IMAGE_ID
,
label
:
LIST_LABEL_IMAGE_ID
},
{
key
:
LIST_KEY_SIZE
,
label
:
LIST_LABEL_SIZE
},
{
key
:
LIST_KEY_LAST_UPDATED
,
label
:
LIST_LABEL_LAST_UPDATED
},
{
key
:
LIST_KEY_ACTIONS
,
label
:
''
},
].
filter
(
f
=>
f
.
key
!==
LIST_KEY_CHECKBOX
||
this
.
isDesktop
);
},
tracking
()
{
tracking
()
{
return
{
return
{
label
:
label
:
...
@@ -126,48 +65,8 @@ export default {
...
@@ -126,48 +65,8 @@ export default {
},
},
methods
:
{
methods
:
{
...
mapActions
([
'
requestTagsList
'
,
'
requestDeleteTag
'
,
'
requestDeleteTags
'
]),
...
mapActions
([
'
requestTagsList
'
,
'
requestDeleteTag
'
,
'
requestDeleteTags
'
]),
formatSize
(
size
)
{
deleteTags
(
toBeDeletedList
)
{
return
numberToHumanSize
(
size
);
this
.
itemsToBeDeleted
=
toBeDeletedList
.
map
(
name
=>
({
},
layers
(
layers
)
{
return
layers
?
n__
(
'
%d layer
'
,
'
%d layers
'
,
layers
)
:
''
;
},
onSelectAllChange
()
{
if
(
this
.
selectAllChecked
)
{
this
.
deselectAll
();
}
else
{
this
.
selectAll
();
}
},
selectAll
()
{
this
.
selectedItems
=
this
.
tags
.
map
(
x
=>
x
.
name
);
this
.
selectAllChecked
=
true
;
},
deselectAll
()
{
this
.
selectedItems
=
[];
this
.
selectAllChecked
=
false
;
},
updateSelectedItems
(
name
)
{
const
delIndex
=
this
.
selectedItems
.
findIndex
(
x
=>
x
===
name
);
if
(
delIndex
>
-
1
)
{
this
.
selectedItems
.
splice
(
delIndex
,
1
);
this
.
selectAllChecked
=
false
;
}
else
{
this
.
selectedItems
.
push
(
name
);
if
(
this
.
selectedItems
.
length
===
this
.
tags
.
length
)
{
this
.
selectAllChecked
=
true
;
}
}
},
deleteSingleItem
(
name
)
{
this
.
itemsToBeDeleted
=
[{
...
this
.
tags
.
find
(
t
=>
t
.
name
===
name
)
}];
this
.
track
(
'
click_button
'
);
this
.
$refs
.
deleteModal
.
show
();
},
deleteMultipleItems
()
{
this
.
itemsToBeDeleted
=
this
.
selectedItems
.
map
(
name
=>
({
...
this
.
tags
.
find
(
t
=>
t
.
name
===
name
),
...
this
.
tags
.
find
(
t
=>
t
.
name
===
name
),
}));
}));
this
.
track
(
'
click_button
'
);
this
.
track
(
'
click_button
'
);
...
@@ -176,7 +75,6 @@ export default {
...
@@ -176,7 +75,6 @@ export default {
handleSingleDelete
()
{
handleSingleDelete
()
{
const
[
itemToDelete
]
=
this
.
itemsToBeDeleted
;
const
[
itemToDelete
]
=
this
.
itemsToBeDeleted
;
this
.
itemsToBeDeleted
=
[];
this
.
itemsToBeDeleted
=
[];
this
.
selectedItems
=
this
.
selectedItems
.
filter
(
name
=>
name
!==
itemToDelete
.
name
);
return
this
.
requestDeleteTag
({
tag
:
itemToDelete
,
params
:
this
.
$route
.
params
.
id
})
return
this
.
requestDeleteTag
({
tag
:
itemToDelete
,
params
:
this
.
$route
.
params
.
id
})
.
then
(()
=>
{
.
then
(()
=>
{
this
.
deleteAlertType
=
ALERT_SUCCESS_TAG
;
this
.
deleteAlertType
=
ALERT_SUCCESS_TAG
;
...
@@ -188,7 +86,6 @@ export default {
...
@@ -188,7 +86,6 @@ export default {
handleMultipleDelete
()
{
handleMultipleDelete
()
{
const
{
itemsToBeDeleted
}
=
this
;
const
{
itemsToBeDeleted
}
=
this
;
this
.
itemsToBeDeleted
=
[];
this
.
itemsToBeDeleted
=
[];
this
.
selectedItems
=
[];
return
this
.
requestDeleteTags
({
return
this
.
requestDeleteTags
({
ids
:
itemsToBeDeleted
.
map
(
x
=>
x
.
name
),
ids
:
itemsToBeDeleted
.
map
(
x
=>
x
.
name
),
...
@@ -227,116 +124,14 @@ export default {
...
@@ -227,116 +124,14 @@ export default {
<details-header
:image-name=
"imageName"
/>
<details-header
:image-name=
"imageName"
/>
<gl-table
:items=
"tags"
:fields=
"fields"
:stacked=
"!isDesktop"
show-empty
>
<tags-table
:tags=
"tags"
:is-loading=
"isLoading"
:is-desktop=
"isDesktop"
@
delete=
"deleteTags"
>
<template
v-if=
"isDesktop"
#head
(
checkbox
)
>
<gl-form-checkbox
ref=
"mainCheckbox"
:checked=
"selectAllChecked"
@
change=
"onSelectAllChange"
/>
</
template
>
<
template
#head
(
actions
)
>
<gl-deprecated-button
ref=
"bulkDeleteButton"
v-gl-tooltip
:disabled=
"!selectedItems || selectedItems.length === 0"
class=
"float-right"
variant=
"danger"
:title=
"$options.i18n.REMOVE_TAGS_BUTTON_TITLE"
:aria-label=
"$options.i18n.REMOVE_TAGS_BUTTON_TITLE"
@
click=
"deleteMultipleItems()"
>
<gl-icon
name=
"remove"
/>
</gl-deprecated-button>
</
template
>
<
template
#cell(checkbox)=
"{item}"
>
<gl-form-checkbox
ref=
"rowCheckbox"
class=
"js-row-checkbox"
:checked=
"selectedItems.includes(item.name)"
@
change=
"updateSelectedItems(item.name)"
/>
</
template
>
<
template
#cell(name)=
"{item, field}"
>
<div
ref=
"rowName"
:class=
"[field.innerClass, 'gl-display-flex']"
>
<span
v-gl-tooltip
data-testid=
"rowNameText"
:title=
"item.name"
class=
"gl-text-overflow-ellipsis gl-overflow-hidden gl-white-space-nowrap"
>
{{
item
.
name
}}
</span>
<clipboard-button
v-if=
"item.location"
ref=
"rowClipboardButton"
:title=
"item.location"
:text=
"item.location"
css-class=
"btn-default btn-transparent btn-clipboard"
/>
</div>
</
template
>
<
template
#cell(short_revision)=
"{value}"
>
<span
ref=
"rowShortRevision"
>
{{
value
}}
</span>
</
template
>
<
template
#cell(total_size)=
"{item}"
>
<span
ref=
"rowSize"
>
{{
formatSize
(
item
.
total_size
)
}}
<template
v-if=
"item.total_size && item.layers"
>
·
</
template
>
{{ layers(item.layers) }}
</span>
</template>
<
template
#cell(created_at)=
"{value}"
>
<span
ref=
"rowTime"
v-gl-tooltip
:title=
"tooltipTitle(value)"
>
{{
timeFormatted
(
value
)
}}
</span>
</
template
>
<
template
#cell(actions)=
"{item}"
>
<gl-deprecated-button
ref=
"singleDeleteButton"
:title=
"$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:aria-label=
"$options.i18n.REMOVE_TAG_BUTTON_TITLE"
:disabled=
"!item.destroy_path"
variant=
"danger"
class=
"js-delete-registry float-right btn-inverted btn-border-color btn-icon"
@
click=
"deleteSingleItem(item.name)"
>
<gl-icon
name=
"remove"
/>
</gl-deprecated-button>
</
template
>
<template
#empty
>
<template
#empty
>
<template
v-if=
"isLoading"
>
<empty-tags-state
:no-containers-image=
"config.noContainersImage"
/>
<gl-skeleton-loader
</
template
>
v-for=
"index in $options.loader.repeat"
<
template
#loader
>
:key=
"index"
<tags-loader
v-once
/>
:width=
"$options.loader.width"
:height=
"$options.loader.height"
preserve-aspect-ratio=
"xMinYMax meet"
>
<rect
width=
"15"
x=
"0"
y=
"12.5"
height=
"15"
rx=
"4"
/>
<rect
width=
"250"
x=
"25"
y=
"10"
height=
"20"
rx=
"4"
/>
<circle
cx=
"290"
cy=
"20"
r=
"10"
/>
<rect
width=
"100"
x=
"315"
y=
"10"
height=
"20"
rx=
"4"
/>
<rect
width=
"100"
x=
"500"
y=
"10"
height=
"20"
rx=
"4"
/>
<rect
width=
"100"
x=
"630"
y=
"10"
height=
"20"
rx=
"4"
/>
<rect
x=
"960"
y=
"0"
width=
"40"
height=
"40"
rx=
"4"
/>
</gl-skeleton-loader>
</
template
>
<gl-empty-state
v-else
:title=
"$options.i18n.EMPTY_IMAGE_REPOSITORY_TITLE"
:svg-path=
"config.noContainersImage"
:description=
"$options.i18n.EMPTY_IMAGE_REPOSITORY_MESSAGE"
class=
"mx-auto my-0"
/>
</
template
>
</
template
>
</
gl
-table>
</
tags
-table>
<gl-pagination
<gl-pagination
v-if=
"!isLoading"
v-if=
"!isLoading"
...
...
app/assets/javascripts/registry/explorer/stores/getters.js
View file @
c8a07385
export
const
tags
=
state
=>
{
// to show the loader inside the table we need to pass an empty array to gl-table whenever the table is loading
// this is to take in account isLoading = true and state.tags =[1,2,3] during pagination and delete
return
state
.
isLoading
?
[]
:
state
.
tags
;
};
export
const
dockerBuildCommand
=
state
=>
{
export
const
dockerBuildCommand
=
state
=>
{
/* eslint-disable @gitlab/require-i18n-strings */
/* eslint-disable @gitlab/require-i18n-strings */
return
`docker build -t
${
state
.
config
.
repositoryUrl
}
.`
;
return
`docker build -t
${
state
.
config
.
repositoryUrl
}
.`
;
...
...
spec/features/groups/container_registry_spec.rb
View file @
c8a07385
...
@@ -75,7 +75,7 @@ describe 'Container Registry', :js do
...
@@ -75,7 +75,7 @@ describe 'Container Registry', :js do
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'latest'
])
{
service
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'latest'
])
{
service
}
click_on
(
class:
'js-delete-registry'
)
first
(
'[data-testid="singleDeleteButton"]'
).
click
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
find
(
'.modal .modal-footer .btn-danger'
).
click
end
end
...
...
spec/features/projects/container_registry_spec.rb
View file @
c8a07385
...
@@ -84,7 +84,7 @@ describe 'Container Registry', :js do
...
@@ -84,7 +84,7 @@ describe 'Container Registry', :js do
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
service
).
to
receive
(
:execute
).
with
(
container_repository
)
{
{
status: :success
}
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'1'
])
{
service
}
expect
(
Projects
::
ContainerRepository
::
DeleteTagsService
).
to
receive
(
:new
).
with
(
container_repository
.
project
,
user
,
tags:
[
'1'
])
{
service
}
first
(
'
.js-delete-registry
'
).
click
first
(
'
[data-testid="singleDeleteButton"]
'
).
click
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
expect
(
find
(
'.modal .modal-title'
)).
to
have_content
_
(
'Remove tag'
)
find
(
'.modal .modal-footer .btn-danger'
).
click
find
(
'.modal .modal-footer .btn-danger'
).
click
end
end
...
...
spec/frontend/registry/explorer/components/details_page/__snapshots__/tags_loader_spec.js.snap
0 → 100644
View file @
c8a07385
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TagsLoader component has the correct markup 1`] = `
<div>
<div
preserve-aspect-ratio="xMinYMax meet"
>
<rect
height="15"
rx="4"
width="15"
x="0"
y="12.5"
/>
<rect
height="20"
rx="4"
width="250"
x="25"
y="10"
/>
<circle
cx="290"
cy="20"
r="10"
/>
<rect
height="20"
rx="4"
width="100"
x="315"
y="10"
/>
<rect
height="20"
rx="4"
width="100"
x="500"
y="10"
/>
<rect
height="20"
rx="4"
width="100"
x="630"
y="10"
/>
<rect
height="40"
rx="4"
width="40"
x="960"
y="0"
/>
</div>
</div>
`;
spec/frontend/registry/explorer/components/details_page/delete_alert_spec.js
View file @
c8a07385
...
@@ -19,6 +19,11 @@ describe('Delete alert', () => {
...
@@ -19,6 +19,11 @@ describe('Delete alert', () => {
wrapper
=
shallowMount
(
component
,
{
stubs
:
{
GlSprintf
},
propsData
});
wrapper
=
shallowMount
(
component
,
{
stubs
:
{
GlSprintf
},
propsData
});
};
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
when deleteAlertType is null
'
,
()
=>
{
describe
(
'
when deleteAlertType is null
'
,
()
=>
{
it
(
'
does not show the alert
'
,
()
=>
{
it
(
'
does not show the alert
'
,
()
=>
{
mountComponent
();
mountComponent
();
...
...
spec/frontend/registry/explorer/components/details_page/delete_modal_spec.js
View file @
c8a07385
...
@@ -23,6 +23,11 @@ describe('Delete Modal', () => {
...
@@ -23,6 +23,11 @@ describe('Delete Modal', () => {
});
});
};
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
contains a GlModal
'
,
()
=>
{
it
(
'
contains a GlModal
'
,
()
=>
{
mountComponent
();
mountComponent
();
expect
(
findModal
().
exists
()).
toBe
(
true
);
expect
(
findModal
().
exists
()).
toBe
(
true
);
...
...
spec/frontend/registry/explorer/components/details_page/details_header_spec.js
View file @
c8a07385
...
@@ -15,6 +15,11 @@ describe('Details Header', () => {
...
@@ -15,6 +15,11 @@ describe('Details Header', () => {
});
});
};
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
has the correct title
'
,
()
=>
{
it
(
'
has the correct title
'
,
()
=>
{
mountComponent
();
mountComponent
();
expect
(
wrapper
.
text
()).
toMatchInterpolatedText
(
DETAILS_PAGE_TITLE
);
expect
(
wrapper
.
text
()).
toMatchInterpolatedText
(
DETAILS_PAGE_TITLE
);
...
...
spec/frontend/registry/explorer/components/details_page/empty_tags_state.js
0 → 100644
View file @
c8a07385
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
component
from
'
~/registry/explorer/components/details_page/empty_tags_state.vue
'
;
import
{
EMPTY_IMAGE_REPOSITORY_TITLE
,
EMPTY_IMAGE_REPOSITORY_MESSAGE
,
}
from
'
~/registry/explorer/constants
'
;
describe
(
'
EmptyTagsState component
'
,
()
=>
{
let
wrapper
;
const
findEmptyState
=
()
=>
wrapper
.
find
(
GlEmptyState
);
const
mountComponent
=
()
=>
{
wrapper
=
shallowMount
(
component
,
{
stubs
:
{
GlEmptyState
,
},
propsData
:
{
noContainersImage
:
'
foo
'
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
contains gl-empty-state
'
,
()
=>
{
mountComponent
();
expect
(
findEmptyState
().
exist
()).
toBe
(
true
);
});
it
(
'
has the correct props
'
,
()
=>
{
mountComponent
();
expect
(
findEmptyState
().
props
()).
toMatchObject
({
title
:
EMPTY_IMAGE_REPOSITORY_TITLE
,
description
:
EMPTY_IMAGE_REPOSITORY_MESSAGE
,
svgPath
:
'
foo
'
,
});
});
});
spec/frontend/registry/explorer/components/details_page/tags_loader_spec.js
0 → 100644
View file @
c8a07385
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
component
from
'
~/registry/explorer/components/details_page/tags_loader.vue
'
;
import
{
GlSkeletonLoader
}
from
'
../../stubs
'
;
describe
(
'
TagsLoader component
'
,
()
=>
{
let
wrapper
;
const
findGlSkeletonLoaders
=
()
=>
wrapper
.
findAll
(
GlSkeletonLoader
);
const
mountComponent
=
()
=>
{
wrapper
=
shallowMount
(
component
,
{
stubs
:
{
GlSkeletonLoader
,
},
// set the repeat to 1 to avoid a long and verbose snapshot
loader
:
{
...
component
.
loader
,
repeat
:
1
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
(
'
produces the correct amount of loaders
'
,
()
=>
{
mountComponent
();
expect
(
findGlSkeletonLoaders
().
length
).
toBe
(
1
);
});
it
(
'
has the correct props
'
,
()
=>
{
mountComponent
();
expect
(
findGlSkeletonLoaders
()
.
at
(
0
)
.
props
(),
).
toMatchObject
({
width
:
component
.
loader
.
width
,
height
:
component
.
loader
.
height
,
});
});
it
(
'
has the correct markup
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
});
spec/frontend/registry/explorer/components/details_page/tags_table_spec.js
0 → 100644
View file @
c8a07385
import
{
mount
}
from
'
@vue/test-utils
'
;
import
stubChildren
from
'
helpers/stub_children
'
;
import
component
from
'
~/registry/explorer/components/details_page/tags_table.vue
'
;
import
{
tagsListResponse
}
from
'
../../mock_data
'
;
describe
(
'
tags_table
'
,
()
=>
{
let
wrapper
;
const
tags
=
[...
tagsListResponse
.
data
];
const
findMainCheckbox
=
()
=>
wrapper
.
find
(
'
[data-testid="mainCheckbox"]
'
);
const
findFirstRowItem
=
testid
=>
wrapper
.
find
(
`[data-testid="
${
testid
}
"]`
);
const
findBulkDeleteButton
=
()
=>
wrapper
.
find
(
'
[data-testid="bulkDeleteButton"]
'
);
const
findAllDeleteButtons
=
()
=>
wrapper
.
findAll
(
'
[data-testid="singleDeleteButton"]
'
);
const
findAllCheckboxes
=
()
=>
wrapper
.
findAll
(
'
[data-testid="rowCheckbox"]
'
);
const
findCheckedCheckboxes
=
()
=>
findAllCheckboxes
().
filter
(
c
=>
c
.
attributes
(
'
checked
'
));
const
findFirsTagColumn
=
()
=>
wrapper
.
find
(
'
.js-tag-column
'
);
const
findFirstTagNameText
=
()
=>
wrapper
.
find
(
'
[data-testid="rowNameText"]
'
);
const
findLoaderSlot
=
()
=>
wrapper
.
find
(
'
[data-testid="loaderSlot"]
'
);
const
findEmptySlot
=
()
=>
wrapper
.
find
(
'
[data-testid="emptySlot"]
'
);
const
mountComponent
=
(
propsData
=
{
tags
,
isDesktop
:
true
})
=>
{
wrapper
=
mount
(
component
,
{
stubs
:
{
...
stubChildren
(
component
),
GlTable
:
false
,
},
propsData
,
slots
:
{
loader
:
'
<div data-testid="loaderSlot"></div>
'
,
empty
:
'
<div data-testid="emptySlot"></div>
'
,
},
});
};
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
it
.
each
([
'
rowCheckbox
'
,
'
rowName
'
,
'
rowShortRevision
'
,
'
rowSize
'
,
'
rowTime
'
,
'
singleDeleteButton
'
,
])(
'
%s exist in the table
'
,
element
=>
{
mountComponent
();
expect
(
findFirstRowItem
(
element
).
exists
()).
toBe
(
true
);
});
describe
(
'
header checkbox
'
,
()
=>
{
it
(
'
exists
'
,
()
=>
{
mountComponent
();
expect
(
findMainCheckbox
().
exists
()).
toBe
(
true
);
});
it
(
'
if selected selects all the rows
'
,
()
=>
{
mountComponent
();
findMainCheckbox
().
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
findMainCheckbox
().
attributes
(
'
checked
'
)).
toBeTruthy
();
expect
(
findCheckedCheckboxes
()).
toHaveLength
(
tags
.
length
);
});
});
it
(
'
if deselect deselects all the row
'
,
()
=>
{
mountComponent
();
findMainCheckbox
().
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
expect
(
findMainCheckbox
().
attributes
(
'
checked
'
)).
toBeTruthy
();
findMainCheckbox
().
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
();
})
.
then
(()
=>
{
expect
(
findMainCheckbox
().
attributes
(
'
checked
'
)).
toBe
(
undefined
);
expect
(
findCheckedCheckboxes
()).
toHaveLength
(
0
);
});
});
});
describe
(
'
row checkbox
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
if selected adds item to selectedItems
'
,
()
=>
{
findFirstRowItem
(
'
rowCheckbox
'
).
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
selectedItems
).
toEqual
([
tags
[
0
].
name
]);
expect
(
findFirstRowItem
(
'
rowCheckbox
'
).
attributes
(
'
checked
'
)).
toBeTruthy
();
});
});
it
(
'
if deselect remove name from selectedItems
'
,
()
=>
{
wrapper
.
setData
({
selectedItems
:
[
tags
[
0
].
name
]
});
findFirstRowItem
(
'
rowCheckbox
'
).
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
selectedItems
.
length
).
toBe
(
0
);
expect
(
findFirstRowItem
(
'
rowCheckbox
'
).
attributes
(
'
checked
'
)).
toBe
(
undefined
);
});
});
});
describe
(
'
header delete button
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
exists
'
,
()
=>
{
expect
(
findBulkDeleteButton
().
exists
()).
toBe
(
true
);
});
it
(
'
is disabled if no item is selected
'
,
()
=>
{
expect
(
findBulkDeleteButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
true
'
);
});
it
(
'
is enabled if at least one item is selected
'
,
()
=>
{
expect
(
findBulkDeleteButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
true
'
);
findFirstRowItem
(
'
rowCheckbox
'
).
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
findBulkDeleteButton
().
attributes
(
'
disabled
'
)).
toBeFalsy
();
});
});
describe
(
'
on click
'
,
()
=>
{
it
(
'
when one item is selected
'
,
()
=>
{
findFirstRowItem
(
'
rowCheckbox
'
).
vm
.
$emit
(
'
change
'
);
findBulkDeleteButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
delete
'
)).
toEqual
([[[
'
centos6
'
]]]);
});
it
(
'
when multiple items are selected
'
,
()
=>
{
findMainCheckbox
().
vm
.
$emit
(
'
change
'
);
findBulkDeleteButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
delete
'
)).
toEqual
([[
tags
.
map
(
t
=>
t
.
name
)]]);
});
});
});
describe
(
'
row delete button
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
exists
'
,
()
=>
{
expect
(
findAllDeleteButtons
()
.
at
(
0
)
.
exists
(),
).
toBe
(
true
);
});
it
(
'
is disabled if the item has no destroy_path
'
,
()
=>
{
expect
(
findAllDeleteButtons
()
.
at
(
1
)
.
attributes
(
'
disabled
'
),
).
toBe
(
'
true
'
);
});
it
(
'
on click
'
,
()
=>
{
findAllDeleteButtons
()
.
at
(
0
)
.
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
delete
'
)).
toEqual
([[[
'
centos6
'
]]]);
});
});
describe
(
'
name cell
'
,
()
=>
{
it
(
'
tag column has a tooltip with the tag name
'
,
()
=>
{
mountComponent
();
expect
(
findFirstTagNameText
().
attributes
(
'
title
'
)).
toBe
(
tagsListResponse
.
data
[
0
].
name
);
});
describe
(
'
on desktop viewport
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
table header has class w-25
'
,
()
=>
{
expect
(
findFirsTagColumn
().
classes
()).
toContain
(
'
w-25
'
);
});
it
(
'
tag column has the mw-m class
'
,
()
=>
{
expect
(
findFirstRowItem
(
'
rowName
'
).
classes
()).
toContain
(
'
mw-m
'
);
});
});
describe
(
'
on mobile viewport
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
tags
,
isDesktop
:
false
});
});
it
(
'
table header does not have class w-25
'
,
()
=>
{
expect
(
findFirsTagColumn
().
classes
()).
not
.
toContain
(
'
w-25
'
);
});
it
(
'
tag column has the gl-justify-content-end class
'
,
()
=>
{
expect
(
findFirstRowItem
(
'
rowName
'
).
classes
()).
toContain
(
'
gl-justify-content-end
'
);
});
});
});
describe
(
'
last updated cell
'
,
()
=>
{
let
timeCell
;
beforeEach
(()
=>
{
mountComponent
();
timeCell
=
findFirstRowItem
(
'
rowTime
'
);
});
it
(
'
displays the time in string format
'
,
()
=>
{
expect
(
timeCell
.
text
()).
toBe
(
'
2 years ago
'
);
});
it
(
'
has a tooltip timestamp
'
,
()
=>
{
expect
(
timeCell
.
attributes
(
'
title
'
)).
toBe
(
'
Sep 19, 2017 1:45pm GMT+0000
'
);
});
});
describe
(
'
empty state slot
'
,
()
=>
{
describe
(
'
when the table is empty
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
tags
:
[],
isDesktop
:
true
});
});
it
(
'
does not show table rows
'
,
()
=>
{
expect
(
findFirstTagNameText
().
exists
()).
toBe
(
false
);
});
it
(
'
has the empty state slot
'
,
()
=>
{
expect
(
findEmptySlot
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
when the table is not empty
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
tags
,
isDesktop
:
true
});
});
it
(
'
does show table rows
'
,
()
=>
{
expect
(
findFirstTagNameText
().
exists
()).
toBe
(
true
);
});
it
(
'
does not show the empty state
'
,
()
=>
{
expect
(
findEmptySlot
().
exists
()).
toBe
(
false
);
});
});
});
describe
(
'
loader slot
'
,
()
=>
{
describe
(
'
when the data is loading
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
isLoading
:
true
,
tags
});
});
it
(
'
show the loader
'
,
()
=>
{
expect
(
findLoaderSlot
().
exists
()).
toBe
(
true
);
});
it
(
'
does not show the table rows
'
,
()
=>
{
expect
(
findFirstTagNameText
().
exists
()).
toBe
(
false
);
});
});
describe
(
'
when the data is not loading
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
isLoading
:
false
,
tags
});
});
it
(
'
does not show the loader
'
,
()
=>
{
expect
(
findLoaderSlot
().
exists
()).
toBe
(
false
);
});
it
(
'
shows the table rows
'
,
()
=>
{
expect
(
findFirstTagNameText
().
exists
()).
toBe
(
true
);
});
});
});
});
spec/frontend/registry/explorer/components/list_page/image_list_spec.js
View file @
c8a07385
...
@@ -24,6 +24,11 @@ describe('Image List', () => {
...
@@ -24,6 +24,11 @@ describe('Image List', () => {
mountComponent
();
mountComponent
();
});
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
=
null
;
});
describe
(
'
list
'
,
()
=>
{
describe
(
'
list
'
,
()
=>
{
it
(
'
contains one list element for each image
'
,
()
=>
{
it
(
'
contains one list element for each image
'
,
()
=>
{
expect
(
findRow
().
length
).
toBe
(
imagesListResponse
.
data
.
length
);
expect
(
findRow
().
length
).
toBe
(
imagesListResponse
.
data
.
length
);
...
...
spec/frontend/registry/explorer/pages/details_spec.js
View file @
c8a07385
import
{
m
ount
}
from
'
@vue/test-utils
'
;
import
{
shallowM
ount
}
from
'
@vue/test-utils
'
;
import
{
Gl
Table
,
GlPagination
,
GlSkeletonLoader
}
from
'
@gitlab/ui
'
;
import
{
Gl
Pagination
}
from
'
@gitlab/ui
'
;
import
Tracking
from
'
~/tracking
'
;
import
Tracking
from
'
~/tracking
'
;
import
stubChildren
from
'
helpers/stub_children
'
;
import
component
from
'
~/registry/explorer/pages/details.vue
'
;
import
component
from
'
~/registry/explorer/pages/details.vue
'
;
import
DeleteAlert
from
'
~/registry/explorer/components/details_page/delete_alert.vue
'
;
import
DeleteAlert
from
'
~/registry/explorer/components/details_page/delete_alert.vue
'
;
import
DeleteModal
from
'
~/registry/explorer/components/details_page/delete_modal.vue
'
;
import
DetailsHeader
from
'
~/registry/explorer/components/details_page/details_header.vue
'
;
import
DetailsHeader
from
'
~/registry/explorer/components/details_page/details_header.vue
'
;
import
TagsLoader
from
'
~/registry/explorer/components/details_page/tags_loader.vue
'
;
import
EmptyTagsState
from
'
~/registry/explorer/components/details_page/empty_tags_state.vue
'
;
import
{
createStore
}
from
'
~/registry/explorer/stores/
'
;
import
{
createStore
}
from
'
~/registry/explorer/stores/
'
;
import
{
import
{
SET_MAIN_LOADING
,
SET_MAIN_LOADING
,
...
@@ -15,7 +15,7 @@ import {
...
@@ -15,7 +15,7 @@ import {
}
from
'
~/registry/explorer/stores/mutation_types/
'
;
}
from
'
~/registry/explorer/stores/mutation_types/
'
;
import
{
tagsListResponse
}
from
'
../mock_data
'
;
import
{
tagsListResponse
}
from
'
../mock_data
'
;
import
{
$toast
}
from
'
../../shared/mock
s
'
;
import
{
TagsTable
,
DeleteModal
}
from
'
../stub
s
'
;
describe
(
'
Details Page
'
,
()
=>
{
describe
(
'
Details Page
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
...
@@ -24,28 +24,19 @@ describe('Details Page', () => {
...
@@ -24,28 +24,19 @@ describe('Details Page', () => {
const
findDeleteModal
=
()
=>
wrapper
.
find
(
DeleteModal
);
const
findDeleteModal
=
()
=>
wrapper
.
find
(
DeleteModal
);
const
findPagination
=
()
=>
wrapper
.
find
(
GlPagination
);
const
findPagination
=
()
=>
wrapper
.
find
(
GlPagination
);
const
findSkeletonLoader
=
()
=>
wrapper
.
find
(
GlSkeletonLoader
);
const
findTagsLoader
=
()
=>
wrapper
.
find
(
TagsLoader
);
const
findMainCheckbox
=
()
=>
wrapper
.
find
({
ref
:
'
mainCheckbox
'
});
const
findTagsTable
=
()
=>
wrapper
.
find
(
TagsTable
);
const
findFirstRowItem
=
ref
=>
wrapper
.
find
({
ref
});
const
findBulkDeleteButton
=
()
=>
wrapper
.
find
({
ref
:
'
bulkDeleteButton
'
});
// findAll and refs seems to no work falling back to class
const
findAllDeleteButtons
=
()
=>
wrapper
.
findAll
(
'
.js-delete-registry
'
);
const
findAllCheckboxes
=
()
=>
wrapper
.
findAll
(
'
.js-row-checkbox
'
);
const
findCheckedCheckboxes
=
()
=>
findAllCheckboxes
().
filter
(
c
=>
c
.
attributes
(
'
checked
'
));
const
findFirsTagColumn
=
()
=>
wrapper
.
find
(
'
.js-tag-column
'
);
const
findFirstTagNameText
=
()
=>
wrapper
.
find
(
'
[data-testid="rowNameText"]
'
);
const
findDeleteAlert
=
()
=>
wrapper
.
find
(
DeleteAlert
);
const
findDeleteAlert
=
()
=>
wrapper
.
find
(
DeleteAlert
);
const
findDetailsHeader
=
()
=>
wrapper
.
find
(
DetailsHeader
);
const
findDetailsHeader
=
()
=>
wrapper
.
find
(
DetailsHeader
);
const
findEmptyTagsState
=
()
=>
wrapper
.
find
(
EmptyTagsState
);
const
routeId
=
window
.
btoa
(
JSON
.
stringify
({
name
:
'
foo
'
,
tags_path
:
'
bar
'
}));
const
routeId
=
window
.
btoa
(
JSON
.
stringify
({
name
:
'
foo
'
,
tags_path
:
'
bar
'
}));
const
mountComponent
=
options
=>
{
const
mountComponent
=
options
=>
{
wrapper
=
m
ount
(
component
,
{
wrapper
=
shallowM
ount
(
component
,
{
store
,
store
,
stubs
:
{
stubs
:
{
...
stubChildren
(
component
),
TagsTable
,
GlSprintf
:
false
,
GlTable
,
DeleteModal
,
DeleteModal
,
},
},
mocks
:
{
mocks
:
{
...
@@ -54,7 +45,6 @@ describe('Details Page', () => {
...
@@ -54,7 +45,6 @@ describe('Details Page', () => {
id
:
routeId
,
id
:
routeId
,
},
},
},
},
$toast
,
},
},
...
options
,
...
options
,
});
});
...
@@ -67,7 +57,6 @@ describe('Details Page', () => {
...
@@ -67,7 +57,6 @@ describe('Details Page', () => {
store
.
commit
(
SET_TAGS_LIST_SUCCESS
,
tagsListResponse
.
data
);
store
.
commit
(
SET_TAGS_LIST_SUCCESS
,
tagsListResponse
.
data
);
store
.
commit
(
SET_TAGS_PAGINATION
,
tagsListResponse
.
headers
);
store
.
commit
(
SET_TAGS_PAGINATION
,
tagsListResponse
.
headers
);
jest
.
spyOn
(
Tracking
,
'
event
'
);
jest
.
spyOn
(
Tracking
,
'
event
'
);
jest
.
spyOn
(
DeleteModal
.
methods
,
'
show
'
);
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
...
@@ -78,18 +67,14 @@ describe('Details Page', () => {
...
@@ -78,18 +67,14 @@ describe('Details Page', () => {
describe
(
'
when isLoading is true
'
,
()
=>
{
describe
(
'
when isLoading is true
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mountComponent
();
mountComponent
();
store
.
dispatch
(
'
receiveTagsListSuccess
'
,
{
...
tagsListResponse
,
data
:
[]
});
store
.
commit
(
SET_MAIN_LOADING
,
true
);
store
.
commit
(
SET_MAIN_LOADING
,
true
);
return
wrapper
.
vm
.
$nextTick
();
});
});
after
All
(()
=>
store
.
commit
(
SET_MAIN_LOADING
,
false
));
after
Each
(()
=>
store
.
commit
(
SET_MAIN_LOADING
,
false
));
it
(
'
has a skeleton loader
'
,
()
=>
{
it
(
'
binds isLoading to tags-table
'
,
()
=>
{
expect
(
findSkeletonLoader
().
exists
()).
toBe
(
true
);
expect
(
findTagsTable
().
props
(
'
isLoading
'
)).
toBe
(
true
);
});
it
(
'
does not have list items
'
,
()
=>
{
expect
(
findFirstRowItem
(
'
rowCheckbox
'
).
exists
()).
toBe
(
false
);
});
});
it
(
'
does not show pagination
'
,
()
=>
{
it
(
'
does not show pagination
'
,
()
=>
{
...
@@ -97,204 +82,76 @@ describe('Details Page', () => {
...
@@ -97,204 +82,76 @@ describe('Details Page', () => {
});
});
});
});
describe
(
'
table
'
,
()
=>
{
describe
(
'
table slots
'
,
()
=>
{
it
.
each
([
beforeEach
(()
=>
{
'
rowCheckbox
'
,
'
rowName
'
,
'
rowShortRevision
'
,
'
rowSize
'
,
'
rowTime
'
,
'
singleDeleteButton
'
,
])(
'
%s exist in the table
'
,
element
=>
{
mountComponent
();
mountComponent
();
expect
(
findFirstRowItem
(
element
).
exists
()).
toBe
(
true
);
});
});
describe
(
'
header checkbox
'
,
()
=>
{
it
(
'
has the empty state
'
,
()
=>
{
beforeEach
(()
=>
{
expect
(
findEmptyTagsState
().
exists
()).
toBe
(
true
);
mountComponent
();
});
it
(
'
exists
'
,
()
=>
{
expect
(
findMainCheckbox
().
exists
()).
toBe
(
true
);
});
it
(
'
if selected set selectedItem and allSelected
'
,
()
=>
{
findMainCheckbox
().
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
findMainCheckbox
().
attributes
(
'
checked
'
)).
toBeTruthy
();
expect
(
findCheckedCheckboxes
()).
toHaveLength
(
store
.
state
.
tags
.
length
);
});
});
it
(
'
if deselect unset selectedItem and allSelected
'
,
()
=>
{
wrapper
.
setData
({
selectedItems
:
[
1
,
2
],
selectAllChecked
:
true
});
findMainCheckbox
().
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
findMainCheckbox
().
attributes
(
'
checked
'
)).
toBe
(
undefined
);
expect
(
findCheckedCheckboxes
()).
toHaveLength
(
0
);
});
});
});
});
describe
(
'
row checkbox
'
,
()
=>
{
it
(
'
has a skeleton loader
'
,
()
=>
{
beforeEach
(()
=>
{
expect
(
findTagsLoader
().
exists
()).
toBe
(
true
);
mountComponent
();
});
it
(
'
if selected adds item to selectedItems
'
,
()
=>
{
findFirstRowItem
(
'
rowCheckbox
'
).
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
selectedItems
).
toEqual
([
store
.
state
.
tags
[
1
].
name
]);
expect
(
findFirstRowItem
(
'
rowCheckbox
'
).
attributes
(
'
checked
'
)).
toBeTruthy
();
});
});
it
(
'
if deselect remove name from selectedItems
'
,
()
=>
{
wrapper
.
setData
({
selectedItems
:
[
store
.
state
.
tags
[
1
].
name
]
});
findFirstRowItem
(
'
rowCheckbox
'
).
vm
.
$emit
(
'
change
'
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
vm
.
selectedItems
.
length
).
toBe
(
0
);
expect
(
findFirstRowItem
(
'
rowCheckbox
'
).
attributes
(
'
checked
'
)).
toBe
(
undefined
);
});
});
});
});
});
describe
(
'
header delete button
'
,
()
=>
{
describe
(
'
table
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mountComponent
();
mountComponent
();
});
});
it
(
'
exists
'
,
()
=>
{
it
(
'
exists
'
,
()
=>
{
mountComponent
();
expect
(
findTagsTable
().
exists
()).
toBe
(
true
);
expect
(
findBulkDeleteButton
().
exists
()).
toBe
(
true
);
});
});
it
(
'
is disabled if no item is selected
'
,
()
=>
{
it
(
'
has the correct props bound
'
,
()
=>
{
mountComponent
();
expect
(
findTagsTable
().
props
()).
toMatchObject
({
expect
(
findBulkDeleteButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
true
'
);
isDesktop
:
true
,
isLoading
:
false
,
tags
:
store
.
state
.
tags
,
});
});
});
it
(
'
is enabled if at least one item is selected
'
,
()
=>
{
describe
(
'
deleteEvent
'
,
()
=>
{
mountComponent
({
data
:
()
=>
({
selectedItems
:
[
store
.
state
.
tags
[
0
].
name
]
})
});
describe
(
'
single item
'
,
()
=>
{
wrapper
.
setData
({
selectedItems
:
[
1
]
});
beforeEach
(()
=>
{
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
findTagsTable
().
vm
.
$emit
(
'
delete
'
,
[
store
.
state
.
tags
[
0
].
name
]);
expect
(
findBulkDeleteButton
().
attributes
(
'
disabled
'
)).
toBeFalsy
();
});
});
});
describe
(
'
on click
'
,
()
=>
{
it
(
'
open the modal
'
,
()
=>
{
it
(
'
when one item is selected
'
,
()
=>
{
mountComponent
({
data
:
()
=>
({
selectedItems
:
[
store
.
state
.
tags
[
0
].
name
]
})
});
jest
.
spyOn
(
wrapper
.
vm
.
$refs
.
deleteModal
,
'
show
'
);
findBulkDeleteButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
([
store
.
state
.
tags
[
0
]]);
expect
(
DeleteModal
.
methods
.
show
).
toHaveBeenCalled
();
expect
(
DeleteModal
.
methods
.
show
).
toHaveBeenCalled
();
expect
(
Tracking
.
event
).
toHaveBeenCalledWith
(
undefined
,
'
click_button
'
,
{
label
:
'
registry_tag_delete
'
,
});
});
});
it
(
'
when multiple items are selected
'
,
()
=>
{
it
(
'
maps the selection to itemToBeDeleted
'
,
()
=>
{
mountComponent
({
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
([
store
.
state
.
tags
[
0
]]);
data
:
()
=>
({
selectedItems
:
store
.
state
.
tags
.
map
(
t
=>
t
.
name
)
}),
});
});
findBulkDeleteButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
(
tagsListResponse
.
data
);
it
(
'
tracks a single delete event
'
,
()
=>
{
expect
(
DeleteModal
.
methods
.
show
).
toHaveBeenCalled
();
expect
(
Tracking
.
event
).
toHaveBeenCalledWith
(
undefined
,
'
click_button
'
,
{
expect
(
Tracking
.
event
).
toHaveBeenCalledWith
(
undefined
,
'
click_button
'
,
{
label
:
'
bulk_
registry_tag_delete
'
,
label
:
'
registry_tag_delete
'
,
});
});
});
});
});
});
});
describe
(
'
row delete button
'
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
();
});
it
(
'
exists
'
,
()
=>
{
describe
(
'
multiple items
'
,
()
=>
{
expect
(
findAllDeleteButtons
()
.
at
(
0
)
.
exists
(),
).
toBe
(
true
);
});
it
(
'
is disabled if the item has no destroy_path
'
,
()
=>
{
expect
(
findAllDeleteButtons
()
.
at
(
1
)
.
attributes
(
'
disabled
'
),
).
toBe
(
'
true
'
);
});
it
(
'
on click
'
,
()
=>
{
findAllDeleteButtons
()
.
at
(
0
)
.
vm
.
$emit
(
'
click
'
);
expect
(
DeleteModal
.
methods
.
show
).
toHaveBeenCalled
();
expect
(
Tracking
.
event
).
toHaveBeenCalledWith
(
undefined
,
'
click_button
'
,
{
label
:
'
registry_tag_delete
'
,
});
});
});
describe
(
'
name cell
'
,
()
=>
{
it
(
'
tag column has a tooltip with the tag name
'
,
()
=>
{
mountComponent
();
expect
(
findFirstTagNameText
().
attributes
(
'
title
'
)).
toBe
(
tagsListResponse
.
data
[
0
].
name
);
});
describe
(
'
on desktop viewport
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mountComponent
(
);
findTagsTable
().
vm
.
$emit
(
'
delete
'
,
store
.
state
.
tags
.
map
(
t
=>
t
.
name
)
);
});
});
it
(
'
table header has class w-25
'
,
()
=>
{
it
(
'
open the modal
'
,
()
=>
{
expect
(
findFirsTagColumn
().
classes
()).
toContain
(
'
w-25
'
);
expect
(
DeleteModal
.
methods
.
show
).
toHaveBeenCalled
(
);
});
});
it
(
'
tag column has the mw-m class
'
,
()
=>
{
it
(
'
maps the selection to itemToBeDeleted
'
,
()
=>
{
expect
(
findFirstRowItem
(
'
rowName
'
).
classes
()).
toContain
(
'
mw-m
'
);
expect
(
wrapper
.
vm
.
itemsToBeDeleted
).
toEqual
(
store
.
state
.
tags
);
});
});
});
describe
(
'
on mobile viewport
'
,
()
=>
{
it
(
'
tracks a single delete event
'
,
()
=>
{
beforeEach
(()
=>
{
expect
(
Tracking
.
event
).
toHaveBeenCalledWith
(
undefined
,
'
click_button
'
,
{
mountComponent
({
label
:
'
bulk_registry_tag_delete
'
,
data
()
{
return
{
isDesktop
:
false
};
},
});
});
});
});
it
(
'
table header does not have class w-25
'
,
()
=>
{
expect
(
findFirsTagColumn
().
classes
()).
not
.
toContain
(
'
w-25
'
);
});
it
(
'
tag column has the gl-justify-content-end class
'
,
()
=>
{
expect
(
findFirstRowItem
(
'
rowName
'
).
classes
()).
toContain
(
'
gl-justify-content-end
'
);
});
});
});
describe
(
'
last updated cell
'
,
()
=>
{
let
timeCell
;
beforeEach
(()
=>
{
mountComponent
();
timeCell
=
findFirstRowItem
(
'
rowTime
'
);
});
it
(
'
displays the time in string format
'
,
()
=>
{
expect
(
timeCell
.
text
()).
toBe
(
'
2 years ago
'
);
});
it
(
'
has a tooltip timestamp
'
,
()
=>
{
expect
(
timeCell
.
attributes
(
'
title
'
)).
toBe
(
'
Sep 19, 2017 1:45pm GMT+0000
'
);
});
});
});
});
});
});
...
@@ -343,44 +200,33 @@ describe('Details Page', () => {
...
@@ -343,44 +200,33 @@ describe('Details Page', () => {
describe
(
'
confirmDelete event
'
,
()
=>
{
describe
(
'
confirmDelete event
'
,
()
=>
{
describe
(
'
when one item is selected to be deleted
'
,
()
=>
{
describe
(
'
when one item is selected to be deleted
'
,
()
=>
{
const
itemsToBeDeleted
=
[{
name
:
'
foo
'
}];
beforeEach
(()
=>
{
mountComponent
();
findTagsTable
().
vm
.
$emit
(
'
delete
'
,
[
store
.
state
.
tags
[
0
].
name
]);
});
it
(
'
dispatch requestDeleteTag with the right parameters
'
,
()
=>
{
it
(
'
dispatch requestDeleteTag with the right parameters
'
,
()
=>
{
mountComponent
({
data
:
()
=>
({
itemsToBeDeleted
})
});
findDeleteModal
().
vm
.
$emit
(
'
confirmDelete
'
);
findDeleteModal
().
vm
.
$emit
(
'
confirmDelete
'
);
expect
(
dispatchSpy
).
toHaveBeenCalledWith
(
'
requestDeleteTag
'
,
{
expect
(
dispatchSpy
).
toHaveBeenCalledWith
(
'
requestDeleteTag
'
,
{
tag
:
itemsToBeDeleted
[
0
],
tag
:
store
.
state
.
tags
[
0
],
params
:
routeId
,
params
:
routeId
,
});
});
});
});
it
(
'
remove the deleted item from the selected items
'
,
()
=>
{
mountComponent
({
data
:
()
=>
({
itemsToBeDeleted
,
selectedItems
:
[
'
foo
'
,
'
bar
'
]
})
});
findDeleteModal
().
vm
.
$emit
(
'
confirmDelete
'
);
expect
(
wrapper
.
vm
.
selectedItems
).
toEqual
([
'
bar
'
]);
});
});
});
describe
(
'
when more than one item is selected to be deleted
'
,
()
=>
{
describe
(
'
when more than one item is selected to be deleted
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mountComponent
({
mountComponent
();
data
:
()
=>
({
findTagsTable
().
vm
.
$emit
(
'
delete
'
,
store
.
state
.
tags
.
map
(
t
=>
t
.
name
));
itemsToBeDeleted
:
[{
name
:
'
foo
'
},
{
name
:
'
bar
'
}],
selectedItems
:
[
'
foo
'
,
'
bar
'
],
}),
});
});
});
it
(
'
dispatch requestDeleteTags with the right parameters
'
,
()
=>
{
it
(
'
dispatch requestDeleteTags with the right parameters
'
,
()
=>
{
findDeleteModal
().
vm
.
$emit
(
'
confirmDelete
'
);
findDeleteModal
().
vm
.
$emit
(
'
confirmDelete
'
);
expect
(
dispatchSpy
).
toHaveBeenCalledWith
(
'
requestDeleteTags
'
,
{
expect
(
dispatchSpy
).
toHaveBeenCalledWith
(
'
requestDeleteTags
'
,
{
ids
:
[
'
foo
'
,
'
bar
'
]
,
ids
:
store
.
state
.
tags
.
map
(
t
=>
t
.
name
)
,
params
:
routeId
,
params
:
routeId
,
});
});
});
});
it
(
'
clears the selectedItems
'
,
()
=>
{
findDeleteModal
().
vm
.
$emit
(
'
confirmDelete
'
);
expect
(
wrapper
.
vm
.
selectedItems
).
toEqual
([]);
});
});
});
});
});
});
});
...
...
spec/frontend/registry/explorer/stores/getters_spec.js
View file @
c8a07385
...
@@ -2,35 +2,6 @@ import * as getters from '~/registry/explorer/stores/getters';
...
@@ -2,35 +2,6 @@ import * as getters from '~/registry/explorer/stores/getters';
describe
(
'
Getters RegistryExplorer store
'
,
()
=>
{
describe
(
'
Getters RegistryExplorer store
'
,
()
=>
{
let
state
;
let
state
;
const
tags
=
[
'
foo
'
,
'
bar
'
];
describe
(
'
tags
'
,
()
=>
{
describe
(
'
when isLoading is false
'
,
()
=>
{
beforeEach
(()
=>
{
state
=
{
tags
,
isLoading
:
false
,
};
});
it
(
'
returns tags
'
,
()
=>
{
expect
(
getters
.
tags
(
state
)).
toEqual
(
state
.
tags
);
});
});
describe
(
'
when isLoading is true
'
,
()
=>
{
beforeEach
(()
=>
{
state
=
{
tags
,
isLoading
:
true
,
};
});
it
(
'
returns empty array
'
,
()
=>
{
expect
(
getters
.
tags
(
state
)).
toEqual
([]);
});
});
});
describe
.
each
`
describe
.
each
`
getter | prefix | configParameter | suffix
getter | prefix | configParameter | suffix
...
...
spec/frontend/registry/explorer/stubs.js
View file @
c8a07385
import
RealTagsTable
from
'
~/registry/explorer/components/details_page/tags_table.vue
'
;
import
RealDeleteModal
from
'
~/registry/explorer/components/details_page/delete_modal.vue
'
;
export
const
GlModal
=
{
export
const
GlModal
=
{
template
:
'
<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>
'
,
template
:
'
<div><slot name="modal-title"></slot><slot></slot><slot name="modal-ok"></slot></div>
'
,
methods
:
{
methods
:
{
...
@@ -14,3 +17,21 @@ export const RouterLink = {
...
@@ -14,3 +17,21 @@ export const RouterLink = {
template
:
`<div><slot></slot></div>`
,
template
:
`<div><slot></slot></div>`
,
props
:
[
'
to
'
],
props
:
[
'
to
'
],
};
};
export
const
TagsTable
=
{
props
:
RealTagsTable
.
props
,
template
:
`<div><slot name="empty"></slot><slot name="loader"></slot></div>`
,
};
export
const
DeleteModal
=
{
template
:
'
<div></div>
'
,
methods
:
{
show
:
jest
.
fn
(),
},
props
:
RealDeleteModal
.
props
,
};
export
const
GlSkeletonLoader
=
{
template
:
`<div><slot></slot></div>`
,
props
:
[
'
width
'
,
'
height
'
],
};
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