Commit 48725c6b authored by Mark Florian's avatar Mark Florian

Merge branch '197918-add-tab-navigation-to-the-package-registry-list-view' into 'master'

Add tab navigation to the package registry list view

See merge request gitlab-org/gitlab!26820
parents 2d713126 a172b543
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { mapState, mapGetters } from 'vuex';
import {
GlTable,
GlPagination,
GlButton,
GlSorting,
GlSortingItem,
GlModal,
GlLink,
GlIcon,
......@@ -14,23 +12,8 @@ import {
import Tracking from '~/tracking';
import { s__, sprintf } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import {
LIST_KEY_NAME,
LIST_KEY_PROJECT,
LIST_KEY_VERSION,
LIST_KEY_PACKAGE_TYPE,
LIST_KEY_CREATED_AT,
LIST_KEY_ACTIONS,
LIST_LABEL_NAME,
LIST_LABEL_PROJECT,
LIST_LABEL_VERSION,
LIST_LABEL_PACKAGE_TYPE,
LIST_LABEL_CREATED_AT,
LIST_LABEL_ACTIONS,
LIST_ORDER_BY_PACKAGE_TYPE,
ASCENDING_ODER,
DESCENDING_ORDER,
} from '../constants';
import { LIST_KEY_ACTIONS, LIST_LABEL_ACTIONS } from '../constants';
import getTableHeaders from '../utils';
import { TrackingActions } from '../../shared/constants';
import { packageTypeToTrackCategory } from '../../shared/utils';
import PackageTags from '../../shared/components/package_tags.vue';
......@@ -40,8 +23,6 @@ export default {
components: {
GlTable,
GlPagination,
GlSorting,
GlSortingItem,
GlButton,
GlLink,
TimeAgoTooltip,
......@@ -63,8 +44,6 @@ export default {
totalItems: state => state.pagination.total,
page: state => state.pagination.page,
isGroupPage: state => state.config.isGroupPage,
orderBy: state => state.sorting.orderBy,
sort: state => state.sorting.sort,
isLoading: 'isLoading',
}),
...mapGetters({ list: 'getList' }),
......@@ -76,61 +55,24 @@ export default {
this.$emit('page:changed', value);
},
},
sortText() {
const field = this.sortableFields.find(s => s.orderBy === this.orderBy);
return field ? field.label : '';
},
isSortAscending() {
return this.sort === ASCENDING_ODER;
},
isListEmpty() {
return !this.list || this.list.length === 0;
},
showActions() {
return !this.isGroupPage;
},
sortableFields() {
// This list is filtered in the case of the project page, and the project column is removed
return [
{
key: LIST_KEY_NAME,
label: LIST_LABEL_NAME,
orderBy: LIST_KEY_NAME,
class: ['text-left'],
},
{
key: LIST_KEY_PROJECT,
label: LIST_LABEL_PROJECT,
orderBy: LIST_KEY_PROJECT,
class: ['text-left'],
},
{
key: LIST_KEY_VERSION,
label: LIST_LABEL_VERSION,
orderBy: LIST_KEY_VERSION,
class: ['text-center'],
},
{
key: LIST_KEY_PACKAGE_TYPE,
label: LIST_LABEL_PACKAGE_TYPE,
orderBy: LIST_ORDER_BY_PACKAGE_TYPE,
class: ['text-center'],
},
{
key: LIST_KEY_CREATED_AT,
label: LIST_LABEL_CREATED_AT,
orderBy: LIST_KEY_CREATED_AT,
class: this.showActions ? ['text-center'] : ['text-right'],
},
].filter(f => f.key !== LIST_KEY_PROJECT || this.isGroupPage);
},
headerFields() {
const actions = {
key: LIST_KEY_ACTIONS,
label: LIST_LABEL_ACTIONS,
tdClass: ['text-right'],
};
return this.showActions ? [...this.sortableFields, actions] : this.sortableFields;
const fields = getTableHeaders(this.isGroupPage);
if (this.showActions) {
fields.push({
key: LIST_KEY_ACTIONS,
label: LIST_LABEL_ACTIONS,
});
}
fields[fields.length - 1].class = ['text-right'];
return fields;
},
modalAction() {
return s__('PackageRegistry|Delete package');
......@@ -157,16 +99,6 @@ export default {
},
},
methods: {
...mapActions(['setSorting']),
onDirectionChange() {
const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ODER;
this.setSorting({ sort });
this.$emit('sort:changed');
},
onSortItemClick(item) {
this.setSorting({ orderBy: item });
this.$emit('sort:changed');
},
setItemToBeDeleted(item) {
this.itemToBeDeleted = { ...item };
this.track(TrackingActions.REQUEST_DELETE_PACKAGE);
......@@ -190,22 +122,6 @@ export default {
<slot v-if="isListEmpty && !isLoading" name="empty-state"></slot>
<template v-else>
<gl-sorting
class="my-3 align-self-end"
:text="sortText"
:is-ascending="isSortAscending"
@sortDirectionChange="onDirectionChange"
>
<gl-sorting-item
v-for="item in sortableFields"
ref="packageListSortItem"
:key="item.key"
@click="onSortItemClick(item.orderBy)"
>
{{ item.label }}
</gl-sorting-item>
</gl-sorting>
<gl-table
:items="list"
:fields="headerFields"
......
<script>
import { mapActions, mapState } from 'vuex';
import { GlEmptyState } from '@gitlab/ui';
import { GlEmptyState, GlTab, GlTabs } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import PackageList from './packages_list.vue';
import PackageSort from './packages_sort.vue';
import { PACKAGE_REGISTRY_TABS } from '../constants';
export default {
components: {
GlEmptyState,
GlTab,
GlTabs,
PackageList,
PackageSort,
},
computed: {
...mapState({
......@@ -28,37 +33,58 @@ export default {
false,
);
},
tabsToRender() {
return PACKAGE_REGISTRY_TABS;
},
},
mounted() {
this.requestPackagesList();
},
methods: {
...mapActions(['requestPackagesList', 'requestDeletePackage']),
...mapActions(['requestPackagesList', 'requestDeletePackage', 'setSelectedType']),
onPageChanged(page) {
return this.requestPackagesList({ page });
},
onPackageDeleteRequest(item) {
return this.requestDeletePackage(item);
},
tabChanged(e) {
const selectedType = PACKAGE_REGISTRY_TABS[e];
this.setSelectedType(selectedType);
this.requestPackagesList();
},
emptyStateTitle({ title, type }) {
if (type) {
return sprintf(s__('PackageRegistry|There are no %{packageType} packages yet'), {
packageType: title,
});
}
return s__('PackageRegistry|There are no packages yet');
},
},
};
</script>
<template>
<package-list
@page:changed="onPageChanged"
@package:delete="onPackageDeleteRequest"
@sort:changed="requestPackagesList"
>
<template #empty-state>
<gl-empty-state
:title="s__('PackageRegistry|There are no packages yet')"
:svg-path="emptyListIllustration"
>
<template #description>
<p v-html="emptyListText"></p>
</template>
</gl-empty-state>
<gl-tabs @input="tabChanged">
<template #tabs-end>
<div class="align-self-center ml-auto">
<package-sort @sort:changed="requestPackagesList" />
</div>
</template>
</package-list>
<gl-tab v-for="(tab, index) in tabsToRender" :key="index" :title="tab.title">
<package-list @page:changed="onPageChanged" @package:delete="onPackageDeleteRequest">
<template #empty-state>
<gl-empty-state :title="emptyStateTitle(tab)" :svg-path="emptyListIllustration">
<template #description>
<p v-html="emptyListText"></p>
</template>
</gl-empty-state>
</template>
</package-list>
</gl-tab>
</gl-tabs>
</template>
<script>
import { GlSorting, GlSortingItem } from '@gitlab/ui';
import { ASCENDING_ODER, DESCENDING_ORDER } from '../constants';
import getTableHeaders from '../utils';
import { mapState, mapActions } from 'vuex';
export default {
name: 'PackageSort',
components: {
GlSorting,
GlSortingItem,
},
computed: {
...mapState({
isGroupPage: state => state.config.isGroupPage,
orderBy: state => state.sorting.orderBy,
sort: state => state.sorting.sort,
}),
sortText() {
const field = this.sortableFields.find(s => s.orderBy === this.orderBy);
return field ? field.label : '';
},
sortableFields() {
return getTableHeaders(this.isGroupPage);
},
isSortAscending() {
return this.sort === ASCENDING_ODER;
},
},
methods: {
...mapActions(['setSorting']),
onDirectionChange() {
const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ODER;
this.setSorting({ sort });
this.$emit('sort:changed');
},
onSortItemClick(item) {
this.setSorting({ orderBy: item });
this.$emit('sort:changed');
},
},
};
</script>
<template>
<gl-sorting
:text="sortText"
:is-ascending="isSortAscending"
@sortDirectionChange="onDirectionChange"
>
<gl-sorting-item
v-for="item in sortableFields"
ref="packageListSortItem"
:key="item.key"
@click="onSortItemClick(item.orderBy)"
>
{{ item.label }}
</gl-sorting-item>
</gl-sorting>
</template>
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import { PackageType } from '../shared/constants';
export const FETCH_PACKAGES_LIST_ERROR_MESSAGE = __(
'Something went wrong while fetching the packages list.',
......@@ -33,3 +34,59 @@ export const DESCENDING_ORDER = 'desc';
// 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';
export const TABLE_HEADER_FIELDS = [
{
key: LIST_KEY_NAME,
label: LIST_LABEL_NAME,
orderBy: LIST_KEY_NAME,
class: ['text-left'],
},
{
key: LIST_KEY_PROJECT,
label: LIST_LABEL_PROJECT,
orderBy: LIST_KEY_PROJECT,
class: ['text-left'],
},
{
key: LIST_KEY_VERSION,
label: LIST_LABEL_VERSION,
orderBy: LIST_KEY_VERSION,
class: ['text-center'],
},
{
key: LIST_KEY_PACKAGE_TYPE,
label: LIST_LABEL_PACKAGE_TYPE,
orderBy: LIST_ORDER_BY_PACKAGE_TYPE,
class: ['text-center'],
},
{
key: LIST_KEY_CREATED_AT,
label: LIST_LABEL_CREATED_AT,
orderBy: LIST_KEY_CREATED_AT,
class: ['text-center'],
},
];
export const PACKAGE_REGISTRY_TABS = [
{
title: __('All'),
type: null,
},
{
title: s__('PackageRegistry|Conan'),
type: PackageType.CONAN,
},
{
title: s__('PackageRegistry|Maven'),
type: PackageType.MAVEN,
},
{
title: s__('PackageRegistry|NPM'),
type: PackageType.NPM,
},
{
title: s__('PackageRegistry|NuGet'),
type: PackageType.NUGET,
},
];
......@@ -14,20 +14,24 @@ import {
export const setInitialState = ({ commit }, data) => commit(types.SET_INITIAL_STATE, data);
export const setLoading = ({ commit }, data) => commit(types.SET_MAIN_LOADING, data);
export const setSorting = ({ commit }, data) => commit(types.SET_SORTING, data);
export const setSelectedType = ({ commit }, data) => commit(types.SET_SELECTED_TYPE, data);
export const receivePackagesListSuccess = ({ commit }, { data, headers }) => {
commit(types.SET_PACKAGE_LIST_SUCCESS, data);
commit(types.SET_PAGINATION, headers);
};
export const requestPackagesList = ({ dispatch, state }, pagination = {}) => {
export const requestPackagesList = ({ dispatch, state }, params = {}) => {
dispatch('setLoading', true);
const { page = DEFAULT_PAGE, per_page = DEFAULT_PAGE_SIZE } = pagination;
const { page = DEFAULT_PAGE, per_page = DEFAULT_PAGE_SIZE } = params;
const { sort, orderBy } = state.sorting;
const type = state.selectedType?.type?.toLowerCase();
const packageType = { package_type: type };
const apiMethod = state.config.isGroupPage ? 'groupPackages' : 'projectPackages';
return Api[apiMethod](state.config.resourceId, {
params: { page, per_page, sort, order_by: orderBy },
params: { page, per_page, sort, order_by: orderBy, ...packageType },
})
.then(({ data, headers }) => {
dispatch('receivePackagesListSuccess', { data, headers });
......
......@@ -4,3 +4,4 @@ export const SET_PACKAGE_LIST_SUCCESS = 'SET_PACKAGE_LIST_SUCCESS';
export const SET_PAGINATION = 'SET_PAGINATION';
export const SET_MAIN_LOADING = 'SET_MAIN_LOADING';
export const SET_SORTING = 'SET_SORTING';
export const SET_SELECTED_TYPE = 'SET_SELECTED_TYPE';
......@@ -26,4 +26,8 @@ export default {
[types.SET_SORTING](state, sorting) {
state.sorting = { ...state.sorting, ...sorting };
},
[types.SET_SELECTED_TYPE](state, type) {
state.selectedType = type;
},
};
import { LIST_KEY_PROJECT, TABLE_HEADER_FIELDS } from './constants';
export default isGroupPage =>
TABLE_HEADER_FIELDS.filter(f => f.key !== LIST_KEY_PROJECT || isGroupPage);
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`packages_list_app renders 1`] = `
<div>
<div
class="row empty-state"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt="There are no packages yet"
class=""
src="helpSvg"
/>
</div>
</div>
<b-tabs-stub
activenavitemclass="gl-tab-nav-item-active gl-tab-nav-item-active-indigo"
class="gl-tabs"
contentclass=",gl-tab-content"
navclass="gl-tabs-nav"
nofade="true"
nonavstyle="true"
tag="div"
>
<template>
<div
class="col-12"
<b-tab-stub
buttonid=""
tag="div"
title="All"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<div
class="row empty-state"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt="There are no packages yet"
class=""
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content"
>
<h4
class="center"
style=""
>
There are no packages yet
</h4>
<p
class="center"
style=""
>
<p>
Learn how to
<a
href="helpUrl"
target="_blank"
>
publish and share your packages
</a>
with GitLab.
</p>
</p>
<div
class="text-center"
>
<!---->
<!---->
</div>
</div>
</div>
</div>
</div>
</template>
</b-tab-stub>
<b-tab-stub
buttonid=""
tag="div"
title="Conan"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<div
class="row empty-state"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt="There are no Conan packages yet"
class=""
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content"
>
<h4
class="center"
style=""
>
There are no Conan packages yet
</h4>
<p
class="center"
style=""
>
<p>
Learn how to
<a
href="helpUrl"
target="_blank"
>
publish and share your packages
</a>
with GitLab.
</p>
</p>
<div
class="text-center"
>
<!---->
<!---->
</div>
</div>
</div>
</div>
</div>
</template>
</b-tab-stub>
<b-tab-stub
buttonid=""
tag="div"
title="Maven"
titlelinkclass="gl-tab-nav-item"
>
<div
class="text-content"
>
<h4
class="center"
style=""
>
There are no packages yet
</h4>
<p
class="center"
style=""
>
<p>
Learn how to
<a
href="helpUrl"
target="_blank"
>
publish and share your packages
</a>
with GitLab.
</p>
</p>
<div
class="text-center"
>
<!---->
<!---->
<template>
<div>
<div
class="row empty-state"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt="There are no Maven packages yet"
class=""
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content"
>
<h4
class="center"
style=""
>
There are no Maven packages yet
</h4>
<p
class="center"
style=""
>
<p>
Learn how to
<a
href="helpUrl"
target="_blank"
>
publish and share your packages
</a>
with GitLab.
</p>
</p>
<div
class="text-center"
>
<!---->
<!---->
</div>
</div>
</div>
</div>
</div>
</template>
</b-tab-stub>
<b-tab-stub
buttonid=""
tag="div"
title="NPM"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<div
class="row empty-state"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt="There are no NPM packages yet"
class=""
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content"
>
<h4
class="center"
style=""
>
There are no NPM packages yet
</h4>
<p
class="center"
style=""
>
<p>
Learn how to
<a
href="helpUrl"
target="_blank"
>
publish and share your packages
</a>
with GitLab.
</p>
</p>
<div
class="text-center"
>
<!---->
<!---->
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</b-tab-stub>
<b-tab-stub
buttonid=""
tag="div"
title="NuGet"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<div
class="row empty-state"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt="There are no NuGet packages yet"
class=""
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content"
>
<h4
class="center"
style=""
>
There are no NuGet packages yet
</h4>
<p
class="center"
style=""
>
<p>
Learn how to
<a
href="helpUrl"
target="_blank"
>
publish and share your packages
</a>
with GitLab.
</p>
</p>
<div
class="text-center"
>
<!---->
<!---->
</div>
</div>
</div>
</div>
</div>
</template>
</b-tab-stub>
</template>
<template>
<div
class="align-self-center ml-auto"
>
<package-sort-stub />
</div>
</div>
</div>
</template>
</b-tabs-stub>
`;
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui';
import { GlEmptyState, GlTab, GlTabs } from '@gitlab/ui';
import PackageListApp from 'ee/packages/list/components/packages_list_app.vue';
const localVue = createLocalVue();
......@@ -18,6 +18,7 @@ describe('packages_list_app', () => {
const emptyListHelpUrl = 'helpUrl';
const findListComponent = () => wrapper.find(PackageList);
const findTabComponent = (index = 0) => wrapper.findAll(GlTab).at(index);
const mountComponent = () => {
wrapper = shallowMount(PackageListApp, {
......@@ -27,6 +28,8 @@ describe('packages_list_app', () => {
GlEmptyState,
GlLoadingIcon,
PackageList,
GlTab,
GlTabs,
},
});
};
......@@ -38,7 +41,7 @@ describe('packages_list_app', () => {
config: {
resourceId: 'project_id',
emptyListIllustration: 'helpSvg',
emptyListHelpUrl: 'helpUrl',
emptyListHelpUrl,
},
},
});
......@@ -55,13 +58,25 @@ describe('packages_list_app', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('generate the correct empty list link', () => {
const emptyState = findListComponent();
const link = emptyState.find('a');
describe('empty state', () => {
it('generate the correct empty list link', () => {
const link = findListComponent().find('a');
expect(link.html()).toMatchInlineSnapshot(
`"<a href=\\"${emptyListHelpUrl}\\" target=\\"_blank\\">publish and share your packages</a>"`,
);
expect(link.element).toMatchInlineSnapshot(`
<a
href="${emptyListHelpUrl}"
target="_blank"
>
publish and share your packages
</a>
`);
});
it('includes the right content on the default tab', () => {
const heading = findListComponent().find('h4');
expect(heading.text()).toBe('There are no packages yet');
});
});
it('call requestPackagesList on page:changed', () => {
......@@ -81,4 +96,18 @@ describe('packages_list_app', () => {
list.vm.$emit('sort:changed');
expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList');
});
describe('tab change', () => {
it('calls requestPackagesList when all tab is clicked', () => {
findTabComponent().trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList');
});
it('calls requestPackagesList when a package type tab is clicked', () => {
findTabComponent(1).trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList');
});
});
});
import Vuex from 'vuex';
import { last } from 'lodash';
import { GlTable, GlSorting, GlPagination, GlModal } from '@gitlab/ui';
import { GlTable, GlPagination, GlModal } from '@gitlab/ui';
import Tracking from '~/tracking';
import { mount, createLocalVue } from '@vue/test-utils';
import PackagesList from 'ee/packages/list/components/packages_list.vue';
......@@ -24,10 +24,8 @@ describe('packages_list', () => {
const findPackagesListLoader = () => wrapper.find(PackagesListLoader);
const findFirstActionColumn = () => wrapper.find({ ref: 'action-delete' });
const findPackageListTable = () => wrapper.find(GlTable);
const findPackageListSorting = () => wrapper.find(GlSorting);
const findPackageListPagination = () => wrapper.find(GlPagination);
const findPackageListDeleteModal = () => wrapper.find(GlModal);
const findSortingItems = () => wrapper.findAll(GlSortingItem);
const findFirstProjectColumn = () => wrapper.find({ ref: 'col-project' });
const findPackageTags = () => wrapper.findAll(PackageTags);
const findEmptySlot = () => wrapper.find({ name: 'empty-slot-stub' });
......@@ -127,11 +125,6 @@ describe('packages_list', () => {
mountComponent();
});
it('contains a sorting component', () => {
const sorting = findPackageListSorting();
expect(sorting.exists()).toBe(true);
});
it('contains a table component', () => {
const sorting = findPackageListTable();
expect(sorting.exists()).toBe(true);
......@@ -222,35 +215,6 @@ describe('packages_list', () => {
});
});
describe('sorting component', () => {
let sorting;
let sortingItems;
beforeEach(() => {
mountComponent();
sorting = findPackageListSorting();
sortingItems = findSortingItems();
});
it('has all the sortable items', () => {
expect(sortingItems.length).toEqual(wrapper.vm.sortableFields.length);
});
it('on sort change set sorting in vuex and emit event', () => {
sorting.vm.$emit('sortDirectionChange');
expect(store.dispatch).toHaveBeenCalledWith('setSorting', { sort: 'asc' });
expect(wrapper.emitted('sort:changed')).toBeTruthy();
});
it('on sort item click set sorting and emit event', () => {
const item = sortingItems.at(0);
const { orderBy } = wrapper.vm.sortableFields[0];
item.vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith('setSorting', { orderBy });
expect(wrapper.emitted('sort:changed')).toBeTruthy();
});
});
describe('pagination component', () => {
let pagination;
let modelEvent;
......
import Vuex from 'vuex';
import { GlSorting } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import PackagesSort from 'ee/packages/list/components/packages_sort.vue';
import stubChildren from 'helpers/stub_children';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('packages_sort', () => {
let wrapper;
let store;
let sorting;
let sortingItems;
const GlSortingItem = { name: 'sorting-item-stub', template: '<div><slot></slot></div>' };
const findPackageListSorting = () => wrapper.find(GlSorting);
const findSortingItems = () => wrapper.findAll(GlSortingItem);
const createStore = isGroupPage => {
const state = {
config: {
isGroupPage,
},
sorting: {
orderBy: 'version',
sort: 'desc',
},
};
store = new Vuex.Store({
state,
});
store.dispatch = jest.fn();
};
const mountComponent = (isGroupPage = false) => {
createStore(isGroupPage);
wrapper = mount(PackagesSort, {
localVue,
store,
stubs: {
...stubChildren(PackagesSort),
GlSortingItem,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when is in projects', () => {
beforeEach(() => {
mountComponent();
sorting = findPackageListSorting();
sortingItems = findSortingItems();
});
it('has all the sortable items', () => {
expect(sortingItems.length).toEqual(wrapper.vm.sortableFields.length);
});
it('on sort change set sorting in vuex and emit event', () => {
sorting.vm.$emit('sortDirectionChange');
expect(store.dispatch).toHaveBeenCalledWith('setSorting', { sort: 'asc' });
expect(wrapper.emitted('sort:changed')).toBeTruthy();
});
it('on sort item click set sorting and emit event', () => {
const item = sortingItems.at(0);
const { orderBy } = wrapper.vm.sortableFields[0];
item.vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith('setSorting', { orderBy });
expect(wrapper.emitted('sort:changed')).toBeTruthy();
});
});
describe('when is in group', () => {
beforeEach(() => {
mountComponent(true);
sorting = findPackageListSorting();
sortingItems = findSortingItems();
});
it('has all the sortable items', () => {
expect(sortingItems.length).toEqual(wrapper.vm.sortableFields.length);
});
});
});
......@@ -72,6 +72,38 @@ describe('Actions Package list store', () => {
);
});
it('should fetch packages of a certain type when selectedType is present', done => {
const packageType = 'maven';
testAction(
actions.requestPackagesList,
undefined,
{
config: { isGroupPage: false, resourceId: 1 },
sorting,
selectedType: { type: packageType },
},
[],
[
{ type: 'setLoading', payload: true },
{ type: 'receivePackagesListSuccess', payload: { data: 'foo', headers } },
{ type: 'setLoading', payload: false },
],
() => {
expect(Api.projectPackages).toHaveBeenCalledWith(1, {
params: {
page: 1,
per_page: 20,
sort: sorting.sort,
order_by: sorting.orderBy,
package_type: packageType,
},
});
done();
},
);
});
it('should create flash on API error', done => {
Api.projectPackages = jest.fn().mockRejectedValue();
testAction(
......
......@@ -77,4 +77,11 @@ describe('Mutations Registry Store', () => {
expect(mockState.sorting).toEqual({ ...mockState.sorting, orderBy: 'foo' });
});
});
describe('SET_SELECTED_TYPE', () => {
it('should set the selected type', () => {
mutations[types.SET_SELECTED_TYPE](mockState, { type: 'maven' });
expect(mockState.selectedType).toEqual({ type: 'maven' });
});
});
});
......@@ -13858,6 +13858,9 @@ msgstr ""
msgid "PackageRegistry|Add NuGet Source"
msgstr ""
msgid "PackageRegistry|Conan"
msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
......@@ -13921,12 +13924,21 @@ msgstr ""
msgid "PackageRegistry|Learn how to %{noPackagesLinkStart}publish and share your packages%{noPackagesLinkEnd} with GitLab."
msgstr ""
msgid "PackageRegistry|Maven"
msgstr ""
msgid "PackageRegistry|Maven Command"
msgstr ""
msgid "PackageRegistry|Maven XML"
msgstr ""
msgid "PackageRegistry|NPM"
msgstr ""
msgid "PackageRegistry|NuGet"
msgstr ""
msgid "PackageRegistry|NuGet Command"
msgstr ""
......@@ -13942,6 +13954,9 @@ msgstr ""
msgid "PackageRegistry|Remove package"
msgstr ""
msgid "PackageRegistry|There are no %{packageType} packages yet"
msgstr ""
msgid "PackageRegistry|There are no packages yet"
msgstr ""
......
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