Commit e2ce61ae authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Paul Slaughter

Remove userCanDeletePackage attribute and prop

- haml binding
- vuex mutation
- vue mutation
- tests
- mock data
- component usage

https://gitlab.com/gitlab-org/gitlab/merge_requests/22096
parent 152d1286
...@@ -46,7 +46,6 @@ export default { ...@@ -46,7 +46,6 @@ export default {
perPage: state => state.pagination.perPage, perPage: state => state.pagination.perPage,
totalItems: state => state.pagination.total, totalItems: state => state.pagination.total,
page: state => state.pagination.page, page: state => state.pagination.page,
canDestroyPackage: state => state.config.canDestroyPackage,
isGroupPage: state => state.config.isGroupPage, isGroupPage: state => state.config.isGroupPage,
}), }),
currentPage: { currentPage: {
...@@ -75,7 +74,7 @@ export default { ...@@ -75,7 +74,7 @@ export default {
return !this.list || this.list.length === 0; return !this.list || this.list.length === 0;
}, },
showActions() { showActions() {
return this.canDestroyPackage; return !this.isGroupPage;
}, },
sortableFields() { sortableFields() {
// This list is filtered in the case of the project page, and the project column is removed // This list is filtered in the case of the project page, and the project column is removed
...@@ -151,7 +150,7 @@ export default { ...@@ -151,7 +150,7 @@ export default {
this.$refs.packageListDeleteModal.show(); this.$refs.packageListDeleteModal.show();
}, },
deleteItemConfirmation() { deleteItemConfirmation() {
this.$emit('package:delete', this.itemToBeDeleted.id); this.$emit('package:delete', this.itemToBeDeleted);
this.track(TrackingActions.DELETE_PACKAGE); this.track(TrackingActions.DELETE_PACKAGE);
this.itemToBeDeleted = null; this.itemToBeDeleted = null;
}, },
...@@ -191,9 +190,13 @@ export default { ...@@ -191,9 +190,13 @@ export default {
:no-local-sorting="true" :no-local-sorting="true"
stacked="md" stacked="md"
> >
<template #name="{value}"> <template #name="{value, item}">
<div ref="col-name" class="flex-truncate-parent"> <div ref="col-name" class="flex-truncate-parent">
<a href="#" class="flex-truncate-child" data-qa-selector="package_link"> <a
:href="item._links.web_path"
class="flex-truncate-child"
data-qa-selector="package_link"
>
{{ value }} {{ value }}
</a> </a>
</div> </div>
...@@ -219,6 +222,7 @@ export default { ...@@ -219,6 +222,7 @@ export default {
variant="danger" variant="danger"
:title="s__('PackageRegistry|Remove package')" :title="s__('PackageRegistry|Remove package')"
:aria-label="s__('PackageRegistry|Remove package')" :aria-label="s__('PackageRegistry|Remove package')"
:disabled="!item._links.delete_api_path"
@click="setItemToBeDeleted(item)" @click="setItemToBeDeleted(item)"
> >
<icon name="remove" /> <icon name="remove" />
......
...@@ -38,8 +38,8 @@ export default { ...@@ -38,8 +38,8 @@ export default {
onPageChanged(page) { onPageChanged(page) {
return this.requestPackagesList({ page }); return this.requestPackagesList({ page });
}, },
onPackageDeleteRequest(packageId) { onPackageDeleteRequest(item) {
return this.requestDeletePackage({ projectId: this.resourceId, packageId }); return this.requestDeletePackage(item);
}, },
}, },
}; };
......
...@@ -25,3 +25,6 @@ export const LIST_LABEL_VERSION = __('Version'); ...@@ -25,3 +25,6 @@ export const LIST_LABEL_VERSION = __('Version');
export const LIST_LABEL_PACKAGE_TYPE = __('Type'); export const LIST_LABEL_PACKAGE_TYPE = __('Type');
export const LIST_LABEL_CREATED_AT = __('Created'); export const LIST_LABEL_CREATED_AT = __('Created');
export const LIST_LABEL_ACTIONS = ''; export const LIST_LABEL_ACTIONS = '';
// The following is not translated because it is used to build a JavaScript exception error message
export const MISSING_DELETE_PATH_ERROR = 'Missing delete_api_path link';
import Api from 'ee/api'; import Api from 'ee/api';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { import {
...@@ -7,6 +8,7 @@ import { ...@@ -7,6 +8,7 @@ import {
DELETE_PACKAGE_SUCCESS_MESSAGE, DELETE_PACKAGE_SUCCESS_MESSAGE,
DEFAULT_PAGE, DEFAULT_PAGE,
DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE,
MISSING_DELETE_PATH_ERROR,
} from '../constants'; } from '../constants';
export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data); export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data);
...@@ -34,9 +36,16 @@ export const requestPackagesList = ({ dispatch, state }, pagination = {}) => { ...@@ -34,9 +36,16 @@ export const requestPackagesList = ({ dispatch, state }, pagination = {}) => {
}); });
}; };
export const requestDeletePackage = ({ dispatch }, { projectId, packageId }) => { export const requestDeletePackage = ({ dispatch }, { _links }) => {
if (!_links || !_links.delete_api_path) {
createFlash(DELETE_PACKAGE_ERROR_MESSAGE);
const error = new Error(MISSING_DELETE_PATH_ERROR);
return Promise.reject(error);
}
dispatch('setLoading', true); dispatch('setLoading', true);
return Api.deleteProjectPackage(projectId, packageId) return axios
.delete(_links.delete_api_path)
.then(() => { .then(() => {
dispatch('requestPackagesList'); dispatch('requestPackagesList');
createFlash(DELETE_PACKAGE_SUCCESS_MESSAGE, 'success'); createFlash(DELETE_PACKAGE_SUCCESS_MESSAGE, 'success');
......
import _ from 'underscore';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { GROUP_PAGE_TYPE } from '../constants'; import { GROUP_PAGE_TYPE } from '../constants';
...@@ -8,9 +7,6 @@ export default { ...@@ -8,9 +7,6 @@ export default {
state.config = { state.config = {
...config, ...config,
isGroupPage: config.pageType === GROUP_PAGE_TYPE, isGroupPage: config.pageType === GROUP_PAGE_TYPE,
canDestroyPackage: !(
_.isNull(config.canDestroyPackage) || _.isUndefined(config.canDestroyPackage)
),
}; };
}, },
......
...@@ -8,7 +8,6 @@ export default () => ({ ...@@ -8,7 +8,6 @@ export default () => ({
* { * {
* resourceId: String, * resourceId: String,
* pageType: String, * pageType: String,
* userCanDelete: Boolean,
* emptyListIllustration: String, * emptyListIllustration: String,
* emptyListHelpUrl: String * emptyListHelpUrl: String
* } * }
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
.col-12 .col-12
#js-vue-packages-list{ data: { resource_id: @project.id, #js-vue-packages-list{ data: { resource_id: @project.id,
page_type: 'projects', page_type: 'projects',
can_destroy_package: can_destroy_package,
empty_list_help_url: help_page_path('administration/packages/index'), empty_list_help_url: help_page_path('administration/packages/index'),
empty_list_illustration: image_path('illustrations/no-packages.svg') } } empty_list_illustration: image_path('illustrations/no-packages.svg') } }
......
...@@ -77,10 +77,7 @@ describe('packages_list_app', () => { ...@@ -77,10 +77,7 @@ describe('packages_list_app', () => {
it('call requestDeletePackage on package:delete', () => { it('call requestDeletePackage on package:delete', () => {
const list = findListComponent(); const list = findListComponent();
list.vm.$emit('package:delete', 1); list.vm.$emit('package:delete', 'foo');
expect(componentConfig.methods.requestDeletePackage).toHaveBeenCalledWith({ expect(componentConfig.methods.requestDeletePackage).toHaveBeenCalledWith('foo');
projectId: 'project_id',
packageId: 1,
});
}); });
}); });
...@@ -53,7 +53,6 @@ describe('packages_list', () => { ...@@ -53,7 +53,6 @@ describe('packages_list', () => {
...mountOptions, ...mountOptions,
computed: { computed: {
...mountOptions.computed, ...mountOptions.computed,
canDestroyPackage: () => false,
isGroupPage: () => true, isGroupPage: () => true,
}, },
}); });
...@@ -63,6 +62,11 @@ describe('packages_list', () => { ...@@ -63,6 +62,11 @@ describe('packages_list', () => {
const projectColumn = findFirstProjectColumn(); const projectColumn = findFirstProjectColumn();
expect(projectColumn.exists()).toBe(true); expect(projectColumn.exists()).toBe(true);
}); });
it('does not show the action column', () => {
const action = findFirstActionColumn();
expect(action.exists()).toBe(false);
});
}); });
it('contains a sorting component', () => { it('contains a sorting component', () => {
...@@ -84,20 +88,6 @@ describe('packages_list', () => { ...@@ -84,20 +88,6 @@ describe('packages_list', () => {
expect(sorting.exists()).toBe(true); expect(sorting.exists()).toBe(true);
}); });
describe('when user can not destroy the package', () => {
beforeEach(() => {
wrapper = mount(PackagesList, {
...mountOptions,
computed: { ...mountOptions.computed, canDestroyPackage: () => false },
});
});
it('does not show the action column', () => {
const action = findFirstActionColumn();
expect(action.exists()).toBe(false);
});
});
describe('when the user can destroy the package', () => { describe('when the user can destroy the package', () => {
it('show the action column', () => { it('show the action column', () => {
const action = findFirstActionColumn(); const action = findFirstActionColumn();
...@@ -131,10 +121,11 @@ describe('packages_list', () => { ...@@ -131,10 +121,11 @@ describe('packages_list', () => {
}); });
it('deleteItemConfirmation emit package:delete', () => { it('deleteItemConfirmation emit package:delete', () => {
wrapper.setData({ itemToBeDeleted: { id: 2 } }); const itemToBeDeleted = { id: 2 };
wrapper.setData({ itemToBeDeleted });
wrapper.vm.deleteItemConfirmation(); wrapper.vm.deleteItemConfirmation();
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(wrapper.emitted('package:delete')).toEqual([[2]]); expect(wrapper.emitted('package:delete')[0]).toEqual([itemToBeDeleted]);
}); });
}); });
......
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Api from 'ee/api';
import createFlash from '~/flash';
import * as actions from 'ee/packages/list/stores/actions'; import * as actions from 'ee/packages/list/stores/actions';
import * as types from 'ee/packages/list/stores/mutation_types'; import * as types from 'ee/packages/list/stores/mutation_types';
import {
MISSING_DELETE_PATH_ERROR,
DELETE_PACKAGE_ERROR_MESSAGE,
} from 'ee/packages/list/constants';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import Api from 'ee/api';
import createFlash from '~/flash';
jest.mock('~/flash.js'); jest.mock('~/flash.js');
jest.mock('ee/api.js'); jest.mock('ee/api.js');
describe('Actions Package list store', () => { describe('Actions Package list store', () => {
const headers = 'bar'; const headers = 'bar';
let mock;
describe('requestPackagesList', () => {
beforeEach(() => { beforeEach(() => {
Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo', headers }); Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo', headers });
Api.groupPackages = jest.fn().mockResolvedValue({ data: 'baz', headers }); Api.groupPackages = jest.fn().mockResolvedValue({ data: 'baz', headers });
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
}); });
describe('requestPackagesList', () => {
it('should fetch the project packages list when isGroupPage is false', done => { it('should fetch the project packages list when isGroupPage is false', done => {
testAction( testAction(
actions.requestPackagesList, actions.requestPackagesList,
...@@ -117,16 +129,18 @@ describe('Actions Package list store', () => { ...@@ -117,16 +129,18 @@ describe('Actions Package list store', () => {
}); });
describe('requestDeletePackage', () => { describe('requestDeletePackage', () => {
it('should call deleteProjectPackage', done => { const payload = {
Api.deleteProjectPackage = jest.fn().mockResolvedValue({ data: 'foo' }); _links: {
delete_api_path: 'foo',
},
};
it('should perform a delete operation on _links.delete_api_path', done => {
mock.onDelete(payload._links.delete_api_path).replyOnce(200);
Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo' }); Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo' });
testAction( testAction(
actions.requestDeletePackage, actions.requestDeletePackage,
{ payload,
projectId: 1,
packageId: 2,
},
null, null,
[], [],
[ [
...@@ -139,13 +153,10 @@ describe('Actions Package list store', () => { ...@@ -139,13 +153,10 @@ describe('Actions Package list store', () => {
}); });
it('should stop the loading and call create flash on api error', done => { it('should stop the loading and call create flash on api error', done => {
Api.deleteProjectPackage = jest.fn().mockRejectedValue(); mock.onDelete(payload._links.delete_api_path).replyOnce(400);
testAction( testAction(
actions.requestDeletePackage, actions.requestDeletePackage,
{ payload,
projectId: 1,
packageId: 2,
},
null, null,
[], [],
[{ type: 'setLoading', payload: true }, { type: 'setLoading', payload: false }], [{ type: 'setLoading', payload: true }, { type: 'setLoading', payload: false }],
...@@ -155,5 +166,17 @@ describe('Actions Package list store', () => { ...@@ -155,5 +166,17 @@ describe('Actions Package list store', () => {
}, },
); );
}); });
it.each`
property | actionPayload
${'_links'} | ${{}}
${'delete_api_path'} | ${{ _links: {} }}
`('should reject and createFlash when $property is missing', ({ actionPayload }, done) => {
testAction(actions.requestDeletePackage, actionPayload, null, [], []).catch(e => {
expect(e).toEqual(new Error(MISSING_DELETE_PATH_ERROR));
expect(createFlash).toHaveBeenCalledWith(DELETE_PACKAGE_ERROR_MESSAGE);
done();
});
});
}); });
}); });
const _links = {
web_path: 'foo',
delete_api_path: 'bar',
};
export const mavenPackage = { export const mavenPackage = {
created_at: '2015-12-10', created_at: '2015-12-10',
id: 1, id: 1,
...@@ -11,6 +16,7 @@ export const mavenPackage = { ...@@ -11,6 +16,7 @@ export const mavenPackage = {
project_id: 1, project_id: 1,
updated_at: '2015-12-10', updated_at: '2015-12-10',
version: '1.0.0', version: '1.0.0',
_links,
}; };
export const mavenFiles = [ export const mavenFiles = [
...@@ -38,6 +44,7 @@ export const npmPackage = { ...@@ -38,6 +44,7 @@ export const npmPackage = {
project_id: 1, project_id: 1,
updated_at: '2015-12-10', updated_at: '2015-12-10',
version: '', version: '',
_links,
}; };
export const npmFiles = [ export const npmFiles = [
...@@ -64,6 +71,7 @@ export const conanPackage = { ...@@ -64,6 +71,7 @@ export const conanPackage = {
recipe: 'conan-package/1.0.0@conan+conan-package/stable', recipe: 'conan-package/1.0.0@conan+conan-package/stable',
updated_at: '2015-12-10', updated_at: '2015-12-10',
version: '1.0.0', version: '1.0.0',
_links,
}; };
export const packageList = [mavenPackage, npmPackage, conanPackage]; export const packageList = [mavenPackage, npmPackage, conanPackage];
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