Commit d498d0e1 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch 'nmezzopera-decouple-store-and-router' into 'master'

Container Registry: decouple store and router

See merge request gitlab-org/gitlab!30196
parents 4060d894 9c00dafb
......@@ -19,7 +19,7 @@ export default () => {
const { endpoint } = el.dataset;
const store = createStore();
const router = createRouter(endpoint, store);
const router = createRouter(endpoint);
store.dispatch('setInitialState', el.dataset);
const attachMainComponent = () =>
......
......@@ -157,6 +157,9 @@ export default {
return config;
},
},
mounted() {
this.requestTagsList({ params: this.$route.params.id });
},
methods: {
...mapActions(['requestTagsList', 'requestDeleteTag', 'requestDeleteTags']),
setModalDescription(itemIndex = -1) {
......
......@@ -103,8 +103,16 @@ export default {
: DELETE_IMAGE_ERROR_MESSAGE;
},
},
mounted() {
this.loadImageList(this.$route.name);
},
methods: {
...mapActions(['requestImagesList', 'requestDeleteImage']),
loadImageList(fromName) {
if (!fromName || !this.images?.length) {
this.requestImagesList();
}
},
deleteImage(item) {
this.track('click_button');
this.itemToDelete = item;
......
......@@ -7,7 +7,7 @@ import { decodeAndParse } from './utils';
Vue.use(VueRouter);
export default function createRouter(base, store) {
export default function createRouter(base) {
const router = new VueRouter({
base,
mode: 'history',
......@@ -20,12 +20,6 @@ export default function createRouter(base, store) {
nameGenerator: () => s__('ContainerRegistry|Container Registry'),
root: true,
},
beforeEnter: (to, from, next) => {
if (!from.name || !store.state.images?.length) {
store.dispatch('requestImagesList');
}
next();
},
},
{
name: 'details',
......@@ -34,10 +28,6 @@ export default function createRouter(base, store) {
meta: {
nameGenerator: route => decodeAndParse(route.params.id).name,
},
beforeEnter: (to, from, next) => {
store.dispatch('requestTagsList', { params: to.params.id });
next();
},
},
],
});
......
......@@ -15,4 +15,5 @@ export const createStore = () =>
mutations,
});
// Deprecated and to be removed
export default createStore();
......@@ -4,7 +4,12 @@ import Tracking from '~/tracking';
import stubChildren from 'helpers/stub_children';
import component from '~/registry/explorer/pages/details.vue';
import { createStore } from '~/registry/explorer/stores/';
import { SET_MAIN_LOADING, SET_INITIAL_STATE } from '~/registry/explorer/stores/mutation_types/';
import {
SET_MAIN_LOADING,
SET_INITIAL_STATE,
SET_TAGS_LIST_SUCCESS,
SET_TAGS_PAGINATION,
} from '~/registry/explorer/stores/mutation_types/';
import {
DELETE_TAG_SUCCESS_MESSAGE,
DELETE_TAG_ERROR_MESSAGE,
......@@ -60,7 +65,9 @@ describe('Details Page', () => {
beforeEach(() => {
store = createStore();
dispatchSpy = jest.spyOn(store, 'dispatch');
store.dispatch('receiveTagsListSuccess', tagsListResponse);
dispatchSpy.mockResolvedValue();
store.commit(SET_TAGS_LIST_SUCCESS, tagsListResponse.data);
store.commit(SET_TAGS_PAGINATION, tagsListResponse.headers);
jest.spyOn(Tracking, 'event');
});
......
import VueRouter from 'vue-router';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import { GlPagination, GlSkeletonLoader, GlSprintf, GlAlert } from '@gitlab/ui';
import Tracking from '~/tracking';
import component from '~/registry/explorer/pages/list.vue';
......@@ -7,22 +6,25 @@ import QuickstartDropdown from '~/registry/explorer/components/quickstart_dropdo
import GroupEmptyState from '~/registry/explorer/components/group_empty_state.vue';
import ProjectEmptyState from '~/registry/explorer/components/project_empty_state.vue';
import ProjectPolicyAlert from '~/registry/explorer/components/project_policy_alert.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_IMAGES_LIST_SUCCESS,
SET_PAGINATION,
SET_INITIAL_STATE,
} from '~/registry/explorer/stores/mutation_types/';
import {
DELETE_IMAGE_SUCCESS_MESSAGE,
DELETE_IMAGE_ERROR_MESSAGE,
} from '~/registry/explorer/constants';
import { imagesListResponse } from '../mock_data';
import { GlModal, GlEmptyState } from '../stubs';
import { GlModal, GlEmptyState, RouterLink } from '../stubs';
import { $toast } from '../../shared/mocks';
const localVue = createLocalVue();
localVue.use(VueRouter);
describe('List Page', () => {
let wrapper;
let dispatchSpy;
let store;
const findDeleteBtn = () => wrapper.find({ ref: 'deleteImageButton' });
const findDeleteModal = () => wrapper.find(GlModal);
......@@ -39,21 +41,31 @@ describe('List Page', () => {
const findProjectPolicyAlert = () => wrapper.find(ProjectPolicyAlert);
const findDeleteAlert = () => wrapper.find(GlAlert);
beforeEach(() => {
const mountComponent = ({ mocks } = {}) => {
wrapper = shallowMount(component, {
localVue,
store,
stubs: {
GlModal,
GlEmptyState,
GlSprintf,
RouterLink,
},
mocks: {
$toast,
$route: {
name: 'foo',
},
...mocks,
},
});
};
beforeEach(() => {
store = createStore();
dispatchSpy = jest.spyOn(store, 'dispatch');
store.dispatch('receiveImagesListSuccess', imagesListResponse);
dispatchSpy.mockResolvedValue();
store.commit(SET_IMAGES_LIST_SUCCESS, imagesListResponse.data);
store.commit(SET_PAGINATION, imagesListResponse.headers);
});
afterEach(() => {
......@@ -61,17 +73,38 @@ describe('List Page', () => {
});
describe('Expiration policy notification', () => {
beforeEach(() => {
mountComponent();
});
it('shows up on project page', () => {
expect(findProjectPolicyAlert().exists()).toBe(true);
});
it('does show up on group page', () => {
store.dispatch('setInitialState', { isGroupPage: true });
store.commit(SET_INITIAL_STATE, { isGroupPage: true });
return wrapper.vm.$nextTick().then(() => {
expect(findProjectPolicyAlert().exists()).toBe(false);
});
});
});
describe('API calls', () => {
it.each`
imageList | name | called
${[]} | ${'foo'} | ${['requestImagesList']}
${imagesListResponse.data} | ${undefined} | ${['requestImagesList']}
${imagesListResponse.data} | ${'foo'} | ${undefined}
`(
'with images equal $imageList and name $name dispatch calls $called',
({ imageList, name, called }) => {
store.commit(SET_IMAGES_LIST_SUCCESS, imageList);
dispatchSpy.mockClear();
mountComponent({ mocks: { $route: { name } } });
expect(dispatchSpy.mock.calls[0]).toEqual(called);
},
);
});
describe('connection error', () => {
const config = {
characterError: true,
......@@ -79,12 +112,13 @@ describe('List Page', () => {
helpPagePath: 'bar',
};
beforeAll(() => {
store.dispatch('setInitialState', config);
beforeEach(() => {
store.commit(SET_INITIAL_STATE, config);
mountComponent();
});
afterAll(() => {
store.dispatch('setInitialState', {});
afterEach(() => {
store.commit(SET_INITIAL_STATE, {});
});
it('should show an empty state', () => {
......@@ -106,9 +140,12 @@ describe('List Page', () => {
});
describe('isLoading is true', () => {
beforeAll(() => store.commit(SET_MAIN_LOADING, true));
beforeEach(() => {
store.commit(SET_MAIN_LOADING, true);
mountComponent();
});
afterAll(() => store.commit(SET_MAIN_LOADING, false));
afterEach(() => store.commit(SET_MAIN_LOADING, false));
it('shows the skeleton loader', () => {
expect(findSkeletonLoader().exists()).toBe(true);
......@@ -125,7 +162,8 @@ describe('List Page', () => {
describe('list is empty', () => {
beforeEach(() => {
store.dispatch('receiveImagesListSuccess', { data: [] });
store.commit(SET_IMAGES_LIST_SUCCESS, []);
mountComponent();
});
it('quick start is not visible', () => {
......@@ -137,12 +175,13 @@ describe('List Page', () => {
});
describe('is group page is true', () => {
beforeAll(() => {
store.dispatch('setInitialState', { isGroupPage: true });
beforeEach(() => {
store.commit(SET_INITIAL_STATE, { isGroupPage: true });
mountComponent();
});
afterAll(() => {
store.dispatch('setInitialState', { isGroupPage: undefined });
afterEach(() => {
store.commit(SET_INITIAL_STATE, { isGroupPage: undefined });
});
it('group empty state is visible', () => {
......@@ -156,6 +195,10 @@ describe('List Page', () => {
});
describe('list is not empty', () => {
beforeEach(() => {
mountComponent();
});
it('quick start is visible', () => {
expect(findQuickStartDropdown().exists()).toBe(true);
});
......
......@@ -9,3 +9,8 @@ export const GlEmptyState = {
template: '<div><slot name="description"></slot></div>',
name: 'GlEmptyStateSTub',
};
export const RouterLink = {
template: `<div><slot></slot></div>`,
props: ['to'],
};
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