Commit e08e167b authored by Illya Klymov's avatar Illya Klymov

Merge branch '336934-improved-ux-for-bulk-deleting-container-image-tags' into 'master'

Fix: refetch the right query after tag deletion

See merge request gitlab-org/gitlab!75007
parents e377022a a3b5e41b
...@@ -46,7 +46,6 @@ export default { ...@@ -46,7 +46,6 @@ export default {
data() { data() {
return { return {
containerRepository: {}, containerRepository: {},
fetchTagsCount: false,
}; };
}, },
apollo: { apollo: {
......
...@@ -9,6 +9,7 @@ query getContainerRepositoryTags( ...@@ -9,6 +9,7 @@ query getContainerRepositoryTags(
) { ) {
containerRepository(id: $id) { containerRepository(id: $id) {
id id
tagsCount
tags(after: $after, before: $before, first: $first, last: $last) { tags(after: $after, before: $before, first: $first, last: $last) {
nodes { nodes {
digest digest
......
...@@ -25,9 +25,11 @@ import { ...@@ -25,9 +25,11 @@ import {
UNFINISHED_STATUS, UNFINISHED_STATUS,
MISSING_OR_DELETED_IMAGE_BREADCRUMB, MISSING_OR_DELETED_IMAGE_BREADCRUMB,
ROOT_IMAGE_TEXT, ROOT_IMAGE_TEXT,
GRAPHQL_PAGE_SIZE,
} from '../constants/index'; } from '../constants/index';
import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_container_repository_tags.mutation.graphql'; import deleteContainerRepositoryTagsMutation from '../graphql/mutations/delete_container_repository_tags.mutation.graphql';
import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql'; import getContainerRepositoryDetailsQuery from '../graphql/queries/get_container_repository_details.query.graphql';
import getContainerRepositoryTagsQuery from '../graphql/queries/get_container_repository_tags.query.graphql';
export default { export default {
name: 'RegistryDetailsPage', name: 'RegistryDetailsPage',
...@@ -133,8 +135,8 @@ export default { ...@@ -133,8 +135,8 @@ export default {
awaitRefetchQueries: true, awaitRefetchQueries: true,
refetchQueries: [ refetchQueries: [
{ {
query: getContainerRepositoryDetailsQuery, query: getContainerRepositoryTagsQuery,
variables: this.queryVariables, variables: { ...this.queryVariables, first: GRAPHQL_PAGE_SIZE },
}, },
], ],
}); });
......
...@@ -167,6 +167,7 @@ export const imageTagsMock = (nodes = tagsMock) => ({ ...@@ -167,6 +167,7 @@ export const imageTagsMock = (nodes = tagsMock) => ({
data: { data: {
containerRepository: { containerRepository: {
id: containerRepositoryMock.id, id: containerRepositoryMock.id,
tagsCount: nodes.length,
tags: { tags: {
nodes, nodes,
pageInfo: { ...tagsPageInfo }, pageInfo: { ...tagsPageInfo },
...@@ -191,7 +192,7 @@ export const graphQLImageDetailsMock = (override) => ({ ...@@ -191,7 +192,7 @@ export const graphQLImageDetailsMock = (override) => ({
data: { data: {
containerRepository: { containerRepository: {
...containerRepositoryMock, ...containerRepositoryMock,
tagsCount: tagsMock.length,
tags: { tags: {
nodes: tagsMock, nodes: tagsMock,
pageInfo: { ...tagsPageInfo }, pageInfo: { ...tagsPageInfo },
......
import { GlKeysetPagination } from '@gitlab/ui'; import { GlKeysetPagination } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { nextTick } from 'vue';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -22,6 +23,7 @@ import { ...@@ -22,6 +23,7 @@ import {
} from '~/packages_and_registries/container_registry/explorer/constants'; } from '~/packages_and_registries/container_registry/explorer/constants';
import deleteContainerRepositoryTagsMutation from '~/packages_and_registries/container_registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql'; import deleteContainerRepositoryTagsMutation from '~/packages_and_registries/container_registry/explorer/graphql/mutations/delete_container_repository_tags.mutation.graphql';
import getContainerRepositoryDetailsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_details.query.graphql'; import getContainerRepositoryDetailsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_details.query.graphql';
import getContainerRepositoryTagsQuery from '~/packages_and_registries/container_registry/explorer/graphql/queries/get_container_repository_tags.query.graphql';
import component from '~/packages_and_registries/container_registry/explorer/pages/details.vue'; import component from '~/packages_and_registries/container_registry/explorer/pages/details.vue';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
...@@ -32,6 +34,7 @@ import { ...@@ -32,6 +34,7 @@ import {
containerRepositoryMock, containerRepositoryMock,
graphQLEmptyImageDetailsMock, graphQLEmptyImageDetailsMock,
tagsMock, tagsMock,
imageTagsMock,
} from '../mock_data'; } from '../mock_data';
import { DeleteModal } from '../stubs'; import { DeleteModal } from '../stubs';
...@@ -67,12 +70,13 @@ describe('Details Page', () => { ...@@ -67,12 +70,13 @@ describe('Details Page', () => {
const waitForApolloRequestRender = async () => { const waitForApolloRequestRender = async () => {
await waitForPromises(); await waitForPromises();
await wrapper.vm.$nextTick(); await nextTick();
}; };
const mountComponent = ({ const mountComponent = ({
resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()), resolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock()),
mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock), mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock),
tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock)),
options, options,
config = {}, config = {},
} = {}) => { } = {}) => {
...@@ -81,6 +85,7 @@ describe('Details Page', () => { ...@@ -81,6 +85,7 @@ describe('Details Page', () => {
const requestHandlers = [ const requestHandlers = [
[getContainerRepositoryDetailsQuery, resolver], [getContainerRepositoryDetailsQuery, resolver],
[deleteContainerRepositoryTagsMutation, mutationResolver], [deleteContainerRepositoryTagsMutation, mutationResolver],
[getContainerRepositoryTagsQuery, tagsResolver],
]; ];
apolloProvider = createMockApollo(requestHandlers); apolloProvider = createMockApollo(requestHandlers);
...@@ -242,38 +247,49 @@ describe('Details Page', () => { ...@@ -242,38 +247,49 @@ describe('Details Page', () => {
describe('confirmDelete event', () => { describe('confirmDelete event', () => {
let mutationResolver; let mutationResolver;
let tagsResolver;
beforeEach(() => { beforeEach(() => {
mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock); mutationResolver = jest.fn().mockResolvedValue(graphQLDeleteImageRepositoryTagsMock);
mountComponent({ mutationResolver }); tagsResolver = jest.fn().mockResolvedValue(graphQLImageDetailsMock(imageTagsMock));
mountComponent({ mutationResolver, tagsResolver });
return waitForApolloRequestRender(); return waitForApolloRequestRender();
}); });
describe('when one item is selected to be deleted', () => { describe('when one item is selected to be deleted', () => {
it('calls apollo mutation with the right parameters', async () => { it('calls apollo mutation with the right parameters and refetches the tags list query', async () => {
findTagsList().vm.$emit('delete', [cleanTags[0]]); findTagsList().vm.$emit('delete', [cleanTags[0]]);
await wrapper.vm.$nextTick(); await nextTick();
findDeleteModal().vm.$emit('confirmDelete'); findDeleteModal().vm.$emit('confirmDelete');
expect(mutationResolver).toHaveBeenCalledWith( expect(mutationResolver).toHaveBeenCalledWith(
expect.objectContaining({ tagNames: [cleanTags[0].name] }), expect.objectContaining({ tagNames: [cleanTags[0].name] }),
); );
await waitForPromises();
expect(tagsResolver).toHaveBeenCalled();
}); });
}); });
describe('when more than one item is selected to be deleted', () => { describe('when more than one item is selected to be deleted', () => {
it('calls apollo mutation with the right parameters', async () => { it('calls apollo mutation with the right parameters and refetches the tags list query', async () => {
findTagsList().vm.$emit('delete', tagsMock); findTagsList().vm.$emit('delete', tagsMock);
await wrapper.vm.$nextTick(); await nextTick();
findDeleteModal().vm.$emit('confirmDelete'); findDeleteModal().vm.$emit('confirmDelete');
expect(mutationResolver).toHaveBeenCalledWith( expect(mutationResolver).toHaveBeenCalledWith(
expect.objectContaining({ tagNames: tagsMock.map((t) => t.name) }), expect.objectContaining({ tagNames: tagsMock.map((t) => t.name) }),
); );
await waitForPromises();
expect(tagsResolver).toHaveBeenCalled();
}); });
}); });
}); });
...@@ -382,7 +398,7 @@ describe('Details Page', () => { ...@@ -382,7 +398,7 @@ describe('Details Page', () => {
findPartialCleanupAlert().vm.$emit('dismiss'); findPartialCleanupAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick(); await nextTick();
expect(axios.post).toHaveBeenCalledWith(config.userCalloutsPath, { expect(axios.post).toHaveBeenCalledWith(config.userCalloutsPath, {
feature_name: config.userCalloutId, feature_name: config.userCalloutId,
...@@ -472,7 +488,7 @@ describe('Details Page', () => { ...@@ -472,7 +488,7 @@ describe('Details Page', () => {
await waitForApolloRequestRender(); await waitForApolloRequestRender();
findDetailsHeader().vm.$emit('delete'); findDetailsHeader().vm.$emit('delete');
await wrapper.vm.$nextTick(); await nextTick();
}; };
it('on delete event it deletes the image', async () => { it('on delete event it deletes the image', async () => {
...@@ -497,13 +513,13 @@ describe('Details Page', () => { ...@@ -497,13 +513,13 @@ describe('Details Page', () => {
findDeleteImage().vm.$emit('start'); findDeleteImage().vm.$emit('start');
await wrapper.vm.$nextTick(); await nextTick();
expect(findTagsLoader().exists()).toBe(true); expect(findTagsLoader().exists()).toBe(true);
findDeleteImage().vm.$emit('end'); findDeleteImage().vm.$emit('end');
await wrapper.vm.$nextTick(); await nextTick();
expect(findTagsLoader().exists()).toBe(false); expect(findTagsLoader().exists()).toBe(false);
}); });
...@@ -513,7 +529,7 @@ describe('Details Page', () => { ...@@ -513,7 +529,7 @@ describe('Details Page', () => {
findDeleteImage().vm.$emit('error'); findDeleteImage().vm.$emit('error');
await wrapper.vm.$nextTick(); await nextTick();
expect(findDeleteAlert().props('deleteAlertType')).toBe(ALERT_DANGER_IMAGE); expect(findDeleteAlert().props('deleteAlertType')).toBe(ALERT_DANGER_IMAGE);
}); });
......
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