Commit d8a6773f authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '214477-reactor-toast-to-alert-in-image-repository-details-page' into 'master'

Refactor toast to alert in Image Repository details

See merge request gitlab-org/gitlab!29685
parents d5d64b73 9d298d61
......@@ -39,14 +39,20 @@ export const DELETE_IMAGE_SUCCESS_MESSAGE = s__(
// Image details page
export const DETAILS_PAGE_TITLE = s__('ContainerRegistry|%{imageName} tags');
export const DELETE_TAG_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while deleting the tag.',
'ContainerRegistry|Something went wrong while marking the tag for deletion.',
);
export const DELETE_TAG_SUCCESS_MESSAGE = s__(
'ContainerRegistry|Tag successfully marked for deletion.',
);
export const DELETE_TAG_SUCCESS_MESSAGE = s__('ContainerRegistry|Tag deleted successfully');
export const DELETE_TAGS_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while deleting the tags.',
'ContainerRegistry|Something went wrong while marking the tags for deletion.',
);
export const DELETE_TAGS_SUCCESS_MESSAGE = s__(
'ContainerRegistry|Tags successfully marked for deletion.',
);
export const DELETE_TAGS_SUCCESS_MESSAGE = s__('ContainerRegistry|Tags deleted successfully');
export const DEFAULT_PAGE = 1;
export const DEFAULT_PAGE_SIZE = 10;
......@@ -65,6 +71,27 @@ export const LIST_LABEL_IMAGE_ID = s__('ContainerRegistry|Image ID');
export const LIST_LABEL_SIZE = s__('ContainerRegistry|Compressed Size');
export const LIST_LABEL_LAST_UPDATED = s__('ContainerRegistry|Last Updated');
export const REMOVE_TAG_BUTTON_TITLE = s__('ContainerRegistry|Remove tag');
export const REMOVE_TAGS_BUTTON_TITLE = s__('ContainerRegistry|Remove selected tags');
export const REMOVE_TAG_CONFIRMATION_TEXT = s__(
`ContainerRegistry|You are about to remove %{item}. Are you sure?`,
);
export const REMOVE_TAGS_CONFIRMATION_TEXT = s__(
`ContainerRegistry|You are about to remove %{item} tags. Are you sure?`,
);
export const EMPTY_IMAGE_REPOSITORY_TITLE = s__('ContainerRegistry|This image has no active tags');
export const EMPTY_IMAGE_REPOSITORY_MESSAGE = s__(
`ContainerRegistry|The last tag related to this image was recently removed.
This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process.
If you have any questions, contact your administrator.`,
);
export const ADMIN_GARBAGE_COLLECTION_TIP = s__(
'ContainerRegistry|Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage.',
);
// Expiration policies
export const EXPIRATION_POLICY_ALERT_TITLE = s__(
......
......@@ -9,12 +9,14 @@ import {
GlPagination,
GlModal,
GlSprintf,
GlAlert,
GlLink,
GlEmptyState,
GlResizeObserverDirective,
GlSkeletonLoader,
} from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { n__, s__ } from '~/locale';
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';
......@@ -35,6 +37,14 @@ import {
DELETE_TAG_ERROR_MESSAGE,
DELETE_TAGS_SUCCESS_MESSAGE,
DELETE_TAGS_ERROR_MESSAGE,
REMOVE_TAG_CONFIRMATION_TEXT,
REMOVE_TAGS_CONFIRMATION_TEXT,
DETAILS_PAGE_TITLE,
REMOVE_TAGS_BUTTON_TITLE,
REMOVE_TAG_BUTTON_TITLE,
EMPTY_IMAGE_REPOSITORY_TITLE,
EMPTY_IMAGE_REPOSITORY_MESSAGE,
ADMIN_GARBAGE_COLLECTION_TIP,
} from '../constants';
export default {
......@@ -49,6 +59,8 @@ export default {
GlSkeletonLoader,
GlSprintf,
GlEmptyState,
GlAlert,
GlLink,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -60,6 +72,19 @@ export default {
width: 1000,
height: 40,
},
i18n: {
DETAILS_PAGE_TITLE,
REMOVE_TAGS_BUTTON_TITLE,
REMOVE_TAG_BUTTON_TITLE,
EMPTY_IMAGE_REPOSITORY_TITLE,
EMPTY_IMAGE_REPOSITORY_MESSAGE,
},
alertMessages: {
success_tag: DELETE_TAG_SUCCESS_MESSAGE,
danger_tag: DELETE_TAG_ERROR_MESSAGE,
success_tags: DELETE_TAGS_SUCCESS_MESSAGE,
danger_tags: DELETE_TAGS_ERROR_MESSAGE,
},
data() {
return {
selectedItems: [],
......@@ -67,6 +92,7 @@ export default {
selectAllChecked: false,
modalDescription: null,
isDesktop: true,
deleteAlertType: false,
};
},
computed: {
......@@ -110,20 +136,40 @@ export default {
this.requestTagsList({ pagination: { page }, params: this.$route.params.id });
},
},
deleteAlertConfig() {
const config = {
title: '',
message: '',
type: 'success',
};
if (this.deleteAlertType) {
[config.type] = this.deleteAlertType.split('_');
const defaultMessage = this.$options.alertMessages[this.deleteAlertType];
if (this.config.isAdmin && config.type === 'success') {
config.title = defaultMessage;
config.message = ADMIN_GARBAGE_COLLECTION_TIP;
} else {
config.message = defaultMessage;
}
}
return config;
},
},
methods: {
...mapActions(['requestTagsList', 'requestDeleteTag', 'requestDeleteTags']),
setModalDescription(itemIndex = -1) {
if (itemIndex === -1) {
this.modalDescription = {
message: s__(`ContainerRegistry|You are about to remove %{item} tags. Are you sure?`),
message: REMOVE_TAGS_CONFIRMATION_TEXT,
item: this.itemsToBeDeleted.length,
};
} else {
const { path } = this.tags[itemIndex];
this.modalDescription = {
message: s__(`ContainerRegistry|You are about to remove %{item}. Are you sure?`),
message: REMOVE_TAG_CONFIRMATION_TEXT,
item: path,
};
}
......@@ -179,19 +225,17 @@ export default {
this.track('click_button');
this.$refs.deleteModal.show();
},
handleSingleDelete(itemToDelete) {
handleSingleDelete(index) {
const itemToDelete = this.tags[index];
this.itemsToBeDeleted = [];
this.selectedItems = this.selectedItems.filter(i => i !== index);
return this.requestDeleteTag({ tag: itemToDelete, params: this.$route.params.id })
.then(() =>
this.$toast.show(DELETE_TAG_SUCCESS_MESSAGE, {
type: 'success',
}),
)
.catch(() =>
this.$toast.show(DELETE_TAG_ERROR_MESSAGE, {
type: 'error',
}),
);
.then(() => {
this.deleteAlertType = 'success_tag';
})
.catch(() => {
this.deleteAlertType = 'danger_tag';
});
},
handleMultipleDelete() {
const { itemsToBeDeleted } = this;
......@@ -202,24 +246,19 @@ export default {
ids: itemsToBeDeleted.map(x => this.tags[x].name),
params: this.$route.params.id,
})
.then(() =>
this.$toast.show(DELETE_TAGS_SUCCESS_MESSAGE, {
type: 'success',
}),
)
.catch(() =>
this.$toast.show(DELETE_TAGS_ERROR_MESSAGE, {
type: 'error',
}),
);
.then(() => {
this.deleteAlertType = 'success_tags';
})
.catch(() => {
this.deleteAlertType = 'danger_tags';
});
},
onDeletionConfirmed() {
this.track('confirm_delete');
if (this.isMultiDelete) {
this.handleMultipleDelete();
} else {
const index = this.itemsToBeDeleted[0];
this.handleSingleDelete(this.tags[index]);
this.handleSingleDelete(this.itemsToBeDeleted[0]);
}
},
handleResize() {
......@@ -231,9 +270,24 @@ export default {
<template>
<div v-gl-resize-observer="handleResize" class="my-3 w-100 slide-enter-to-element">
<gl-alert
v-if="deleteAlertType"
:variant="deleteAlertConfig.type"
:title="deleteAlertConfig.title"
class="my-2"
@dismiss="deleteAlertType = null"
>
<gl-sprintf :message="deleteAlertConfig.message">
<template #docLink="{content}">
<gl-link :href="config.garbageCollectionHelpPagePath" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<div class="d-flex my-3 align-items-center">
<h4>
<gl-sprintf :message="s__('ContainerRegistry|%{imageName} tags')">
<gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE">
<template #imageName>
{{ imageName }}
</template>
......@@ -256,8 +310,8 @@ export default {
:disabled="!selectedItems || selectedItems.length === 0"
class="float-right"
variant="danger"
:title="s__('ContainerRegistry|Remove selected tags')"
:aria-label="s__('ContainerRegistry|Remove selected tags')"
:title="$options.i18n.REMOVE_TAGS_BUTTON_TITLE"
:aria-label="$options.i18n.REMOVE_TAGS_BUTTON_TITLE"
@click="deleteMultipleItems()"
>
<gl-icon name="remove" />
......@@ -306,8 +360,8 @@ export default {
<template #cell(actions)="{index, item}">
<gl-deprecated-button
ref="singleDeleteButton"
:title="s__('ContainerRegistry|Remove tag')"
:aria-label="s__('ContainerRegistry|Remove tag')"
: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"
......@@ -337,15 +391,9 @@ export default {
</template>
<gl-empty-state
v-else
:title="s__('ContainerRegistry|This image has no active tags')"
:title="$options.i18n.EMPTY_IMAGE_REPOSITORY_TITLE"
:svg-path="config.noContainersImage"
:description="
s__(
`ContainerRegistry|The last tag related to this image was recently removed.
This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process.
If you have any questions, contact your administrator.`,
)
"
:description="$options.i18n.EMPTY_IMAGE_REPOSITORY_MESSAGE"
class="mx-auto my-0"
/>
</template>
......
<script>
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
import { s__ } from '~/locale';
export default {
components: {
GlAlert,
GlSprintf,
GlLink,
},
i18n: {
garbageCollectionTipText: s__(
'ContainerRegistry|This Registry contains deleted image tag data. Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage.',
),
},
computed: {
...mapState(['config']),
...mapGetters(['showGarbageCollection']),
},
methods: {
...mapActions(['setShowGarbageCollectionTip']),
},
};
export default {};
</script>
<template>
<div>
<gl-alert
v-if="showGarbageCollection"
variant="tip"
class="my-2"
@dismiss="setShowGarbageCollectionTip(false)"
>
<gl-sprintf :message="$options.i18n.garbageCollectionTipText">
<template #docLink="{content}">
<gl-link :href="config.garbageCollectionHelpPagePath" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<transition name="slide">
<router-view ref="router-view" />
</transition>
......
......@@ -66,7 +66,7 @@ export const requestDeleteTag = ({ commit, dispatch, state }, { tag, params }) =
dispatch('setShowGarbageCollectionTip', true);
return dispatch('requestTagsList', { pagination: state.tagsPagination, params });
})
.catch(() => {
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
......@@ -83,7 +83,7 @@ export const requestDeleteTags = ({ commit, dispatch, state }, { ids, params })
dispatch('setShowGarbageCollectionTip', true);
return dispatch('requestTagsList', { pagination: state.tagsPagination, params });
})
.catch(() => {
.finally(() => {
commit(types.SET_MAIN_LOADING, false);
});
};
......
---
title: Use alerts instead of toasts in Image Repository details
merge_request: 29685
author:
type: changed
......@@ -5713,6 +5713,9 @@ msgstr ""
msgid "ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}"
msgstr ""
msgid "ContainerRegistry|Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage."
msgstr ""
msgid "ContainerRegistry|Remove repository"
msgstr ""
......@@ -5727,19 +5730,19 @@ msgstr[1] ""
msgid "ContainerRegistry|Retention policy has been Enabled"
msgstr ""
msgid "ContainerRegistry|Something went wrong while deleting the tag."
msgid "ContainerRegistry|Something went wrong while fetching the expiration policy."
msgstr ""
msgid "ContainerRegistry|Something went wrong while deleting the tags."
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the expiration policy."
msgid "ContainerRegistry|Something went wrong while fetching the tags list."
msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the repository list."
msgid "ContainerRegistry|Something went wrong while marking the tag for deletion."
msgstr ""
msgid "ContainerRegistry|Something went wrong while fetching the tags list."
msgid "ContainerRegistry|Something went wrong while marking the tags for deletion."
msgstr ""
msgid "ContainerRegistry|Something went wrong while scheduling %{title} for deletion. Please try again."
......@@ -5751,16 +5754,16 @@ msgstr ""
msgid "ContainerRegistry|Tag"
msgstr ""
msgid "ContainerRegistry|Tag deleted successfully"
msgstr ""
msgid "ContainerRegistry|Tag expiration policy"
msgstr ""
msgid "ContainerRegistry|Tag expiration policy is designed to:"
msgstr ""
msgid "ContainerRegistry|Tags deleted successfully"
msgid "ContainerRegistry|Tag successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags successfully marked for deletion."
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
......@@ -5793,9 +5796,6 @@ msgstr ""
msgid "ContainerRegistry|There was an error during the deletion of this image repository, please try again."
msgstr ""
msgid "ContainerRegistry|This Registry contains deleted image tag data. Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage."
msgstr ""
msgid "ContainerRegistry|This image has no active tags"
msgstr ""
......
import { mount } from '@vue/test-utils';
import { GlTable, GlPagination, GlSkeletonLoader } from '@gitlab/ui';
import { GlTable, GlPagination, GlSkeletonLoader, GlAlert, GlLink } from '@gitlab/ui';
import Tracking from '~/tracking';
import stubChildren from 'helpers/stub_children';
import component from '~/registry/explorer/pages/details.vue';
import store from '~/registry/explorer/stores/';
import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/';
import { createStore } from '~/registry/explorer/stores/';
import { SET_MAIN_LOADING, SET_INITIAL_STATE } from '~/registry/explorer/stores/mutation_types/';
import {
DELETE_TAG_SUCCESS_MESSAGE,
DELETE_TAG_ERROR_MESSAGE,
DELETE_TAGS_SUCCESS_MESSAGE,
DELETE_TAGS_ERROR_MESSAGE,
ADMIN_GARBAGE_COLLECTION_TIP,
} from '~/registry/explorer/constants';
import { tagsListResponse } from '../mock_data';
import { GlModal } from '../stubs';
......@@ -18,6 +19,7 @@ import { $toast } from '../../shared/mocks';
describe('Details Page', () => {
let wrapper;
let dispatchSpy;
let store;
const findDeleteModal = () => wrapper.find(GlModal);
const findPagination = () => wrapper.find(GlPagination);
......@@ -30,6 +32,7 @@ describe('Details Page', () => {
const findAllCheckboxes = () => wrapper.findAll('.js-row-checkbox');
const findCheckedCheckboxes = () => findAllCheckboxes().filter(c => c.attributes('checked'));
const findFirsTagColumn = () => wrapper.find('.js-tag-column');
const findAlert = () => wrapper.find(GlAlert);
const routeId = window.btoa(JSON.stringify({ name: 'foo', tags_path: 'bar' }));
......@@ -55,6 +58,7 @@ describe('Details Page', () => {
};
beforeEach(() => {
store = createStore();
dispatchSpy = jest.spyOn(store, 'dispatch');
store.dispatch('receiveTagsListSuccess', tagsListResponse);
jest.spyOn(Tracking, 'event');
......@@ -62,6 +66,7 @@ describe('Details Page', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when isLoading is true', () => {
......@@ -328,25 +333,9 @@ describe('Details Page', () => {
});
// itemsToBeDeleted is not represented in the DOM, is used as parking variable between selected and deleted items
expect(wrapper.vm.itemsToBeDeleted).toEqual([]);
expect(wrapper.vm.selectedItems).toEqual([]);
expect(findCheckedCheckboxes()).toHaveLength(0);
});
it('show success toast on successful delete', () => {
return wrapper.vm.handleSingleDelete(0).then(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAG_SUCCESS_MESSAGE, {
type: 'success',
});
});
});
it('show error toast on erred delete', () => {
dispatchSpy.mockRejectedValue();
return wrapper.vm.handleSingleDelete(0).then(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAG_ERROR_MESSAGE, {
type: 'error',
});
});
});
});
describe('when multiple elements are selected', () => {
......@@ -365,34 +354,121 @@ describe('Details Page', () => {
expect(wrapper.vm.itemsToBeDeleted).toEqual([]);
expect(findCheckedCheckboxes()).toHaveLength(0);
});
});
});
it('tracks cancel_delete when cancel event is emitted', () => {
const deleteModal = findDeleteModal();
deleteModal.vm.$emit('cancel');
return wrapper.vm.$nextTick().then(() => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', {
label: 'registry_tag_delete',
});
});
});
});
it('show success toast on successful delete', () => {
return wrapper.vm.handleMultipleDelete(0).then(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAGS_SUCCESS_MESSAGE, {
type: 'success',
describe('Delete alert', () => {
const config = {
garbageCollectionHelpPagePath: 'foo',
};
describe('when the user is an admin', () => {
beforeEach(() => {
store.commit(SET_INITIAL_STATE, { ...config, isAdmin: true });
});
afterEach(() => {
store.commit(SET_INITIAL_STATE, config);
});
describe.each`
deleteType | successTitle | errorTitle
${'handleSingleDelete'} | ${DELETE_TAG_SUCCESS_MESSAGE} | ${DELETE_TAG_ERROR_MESSAGE}
${'handleMultipleDelete'} | ${DELETE_TAGS_SUCCESS_MESSAGE} | ${DELETE_TAGS_ERROR_MESSAGE}
`('behaves correctly on $deleteType', ({ deleteType, successTitle, errorTitle }) => {
describe('when delete is successful', () => {
beforeEach(() => {
dispatchSpy.mockResolvedValue();
mountComponent();
return wrapper.vm[deleteType]('foo');
});
it('alert exists', () => {
expect(findAlert().exists()).toBe(true);
});
it('show error toast on erred delete', () => {
it('alert body contains admin tip', () => {
expect(
findAlert()
.text()
.replace(/\s\s+/gm, ' '),
).toBe(ADMIN_GARBAGE_COLLECTION_TIP.replace(/%{\w+}/gm, ''));
});
it('alert body contains link', () => {
const alertLink = findAlert().find(GlLink);
expect(alertLink.exists()).toBe(true);
expect(alertLink.attributes('href')).toBe(config.garbageCollectionHelpPagePath);
});
it('alert title is appropriate', () => {
expect(findAlert().attributes('title')).toBe(successTitle);
});
});
describe('when delete is not successful', () => {
beforeEach(() => {
mountComponent();
dispatchSpy.mockRejectedValue();
return wrapper.vm.handleMultipleDelete(0).then(() => {
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAGS_ERROR_MESSAGE, {
type: 'error',
return wrapper.vm[deleteType]('foo');
});
it('alert exist and text is appropriate', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(errorTitle);
});
});
});
});
it('tracks cancel_delete when cancel event is emitted', () => {
const deleteModal = findDeleteModal();
deleteModal.vm.$emit('cancel');
return wrapper.vm.$nextTick().then(() => {
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'cancel_delete', {
label: 'registry_tag_delete',
describe.each`
deleteType | successTitle | errorTitle
${'handleSingleDelete'} | ${DELETE_TAG_SUCCESS_MESSAGE} | ${DELETE_TAG_ERROR_MESSAGE}
${'handleMultipleDelete'} | ${DELETE_TAGS_SUCCESS_MESSAGE} | ${DELETE_TAGS_ERROR_MESSAGE}
`(
'when the user is not an admin alert behaves correctly on $deleteType',
({ deleteType, successTitle, errorTitle }) => {
beforeEach(() => {
store.commit('SET_INITIAL_STATE', { ...config });
});
describe('when delete is successful', () => {
beforeEach(() => {
dispatchSpy.mockResolvedValue();
mountComponent();
return wrapper.vm[deleteType]('foo');
});
it('alert exist and text is appropriate', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(successTitle);
});
});
describe('when delete is not successful', () => {
beforeEach(() => {
mountComponent();
dispatchSpy.mockRejectedValue();
return wrapper.vm[deleteType]('foo');
});
it('alert exist and text is appropriate', () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(errorTitle);
});
});
},
);
});
});
import { shallowMount } from '@vue/test-utils';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import component from '~/registry/explorer/pages/index.vue';
import store from '~/registry/explorer/stores/';
describe('List Page', () => {
let wrapper;
let dispatchSpy;
const findRouterView = () => wrapper.find({ ref: 'router-view' });
const findAlert = () => wrapper.find(GlAlert);
const findLink = () => wrapper.find(GlLink);
const mountComponent = () => {
wrapper = shallowMount(component, {
store,
stubs: {
RouterView: true,
GlSprintf,
},
});
};
beforeEach(() => {
dispatchSpy = jest.spyOn(store, 'dispatch');
mountComponent();
});
it('has a router view', () => {
expect(findRouterView().exists()).toBe(true);
});
describe('garbageCollectionTip alert', () => {
beforeEach(() => {
store.dispatch('setInitialState', { isAdmin: true, garbageCollectionHelpPagePath: 'foo' });
store.dispatch('setShowGarbageCollectionTip', true);
});
afterEach(() => {
store.dispatch('setInitialState', {});
store.dispatch('setShowGarbageCollectionTip', false);
});
it('is visible when the user is an admin and the user performed a delete action', () => {
expect(findAlert().exists()).toBe(true);
});
it('on dismiss disappears ', () => {
findAlert().vm.$emit('dismiss');
expect(dispatchSpy).toHaveBeenCalledWith('setShowGarbageCollectionTip', false);
return wrapper.vm.$nextTick().then(() => {
expect(findAlert().exists()).toBe(false);
});
});
it('contains a link to the docs', () => {
const link = findLink();
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe(store.state.config.garbageCollectionHelpPagePath);
});
});
});
......@@ -191,7 +191,10 @@ describe('Actions RegistryExplorer Store', () => {
{
tagsPagination: {},
},
[{ type: types.SET_MAIN_LOADING, payload: true }],
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[
{
type: 'setShowGarbageCollectionTip',
......@@ -220,8 +223,7 @@ describe('Actions RegistryExplorer Store', () => {
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
done,
);
).catch(() => done());
});
});
......@@ -241,7 +243,10 @@ describe('Actions RegistryExplorer Store', () => {
{
tagsPagination: {},
},
[{ type: types.SET_MAIN_LOADING, payload: true }],
[
{ type: types.SET_MAIN_LOADING, payload: true },
{ type: types.SET_MAIN_LOADING, payload: false },
],
[
{
type: 'setShowGarbageCollectionTip',
......@@ -273,8 +278,7 @@ describe('Actions RegistryExplorer Store', () => {
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
done,
);
).catch(() => done());
});
});
......@@ -311,9 +315,7 @@ describe('Actions RegistryExplorer Store', () => {
{ type: types.SET_MAIN_LOADING, payload: false },
],
[],
).catch(() => {
done();
});
).catch(() => done());
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment