Commit d8d79922 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Andrew Fontaine

Finalise tabs removal from pakcage_list_app

- source
- tests
- snapshots
parent 57c07954
<script>
import { GlSorting, GlSortingItem } from '@gitlab/ui';
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { __, s__ } from '~/locale';
import PackageTypeToken from './tokens/package_type_token.vue';
import { ASCENDING_ODER, DESCENDING_ORDER } from '../constants';
import getTableHeaders from '../utils';
export default {
name: 'PackageSort',
components: {
GlSorting,
GlSortingItem,
GlFilteredSearch,
},
computed: {
...mapState({
isGroupPage: (state) => state.config.isGroupPage,
orderBy: (state) => state.sorting.orderBy,
sort: (state) => state.sorting.sort,
filter: (state) => state.filter,
}),
internalFilter: {
get() {
return this.filter;
},
set(value) {
this.setFilter(value);
},
},
sortText() {
const field = this.sortableFields.find((s) => s.orderBy === this.orderBy);
return field ? field.label : '';
......@@ -26,9 +37,21 @@ export default {
isSortAscending() {
return this.sort === ASCENDING_ODER;
},
tokens() {
return [
{
type: 'type',
icon: 'package',
title: s__('PackageRegistry|Type'),
unique: true,
token: PackageTypeToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
},
];
},
},
methods: {
...mapActions(['setSorting']),
...mapActions(['setSorting', 'setFilter']),
onDirectionChange() {
const sort = this.isSortAscending ? DESCENDING_ORDER : ASCENDING_ODER;
this.setSorting({ sort });
......@@ -38,11 +61,24 @@ export default {
this.setSorting({ orderBy: item });
this.$emit('sort:changed');
},
clearSearch() {
this.setFilter([]);
this.$emit('filter:changed');
},
},
};
</script>
<template>
<div class="gl-display-flex gl-p-5 gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100">
<gl-filtered-search
v-model="internalFilter"
class="gl-mr-4 gl-flex-fill-1"
:placeholder="__('Filter results')"
:available-tokens="tokens"
@submit="$emit('filter:changed')"
@clear="clearSearch"
/>
<gl-sorting
:text="sortText"
:is-ascending="isSortAscending"
......@@ -57,4 +93,5 @@ export default {
{{ item.label }}
</gl-sorting-item>
</gl-sorting>
</div>
</template>
<script>
import { GlSearchBoxByClick } from '@gitlab/ui';
import { mapActions } from 'vuex';
export default {
components: {
GlSearchBoxByClick,
},
methods: {
...mapActions(['setFilter']),
},
};
</script>
<template>
<gl-search-box-by-click
:placeholder="s__('PackageRegistry|Filter by name')"
@submit="$emit('filter')"
@input="setFilter"
/>
</template>
<script>
import { mapActions, mapState } from 'vuex';
import { GlEmptyState, GlTab, GlTabs, GlLink, GlSprintf } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import createFlash from '~/flash';
import { historyReplaceState } from '~/lib/utils/common_utils';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import PackageFilter from './packages_filter.vue';
import PackageList from './packages_list.vue';
import PackageSort from './packages_sort.vue';
import { PACKAGE_REGISTRY_TABS, DELETE_PACKAGE_SUCCESS_MESSAGE } from '../constants';
import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '../constants';
import PackageTitle from './package_title.vue';
import PackageSearch from './package_search.vue';
export default {
components: {
GlEmptyState,
GlTab,
GlTabs,
GlLink,
GlSprintf,
PackageFilter,
PackageList,
PackageSort,
PackageTitle,
PackageSearch,
},
computed: {
...mapState({
emptyListIllustration: (state) => state.config.emptyListIllustration,
emptyListHelpUrl: (state) => state.config.emptyListHelpUrl,
filterQuery: (state) => state.filterQuery,
filter: (state) => state.filter,
selectedType: (state) => state.selectedType,
packageHelpUrl: (state) => state.config.packageHelpUrl,
packagesCount: (state) => state.pagination?.total,
}),
tabsToRender() {
return PACKAGE_REGISTRY_TABS;
emptySearch() {
return (
this.filter.filter((f) => f.type !== 'filtered-search-term' || f.value?.data).length === 0
);
},
emptyStateTitle() {
return this.emptySearch
? s__('PackageRegistry|There are no packages yet')
: s__('PackageRegistry|Sorry, your filter produced no results');
},
},
mounted() {
......@@ -48,27 +52,6 @@ export default {
onPackageDeleteRequest(item) {
return this.requestDeletePackage(item);
},
tabChanged(index) {
const selected = PACKAGE_REGISTRY_TABS[index];
if (selected !== this.selectedType) {
this.setSelectedType(selected);
this.requestPackagesList();
}
},
emptyStateTitle({ title, type }) {
if (this.filterQuery) {
return s__('PackageRegistry|Sorry, your filter produced no results');
}
if (type) {
return sprintf(s__('PackageRegistry|There are no %{packageType} packages yet'), {
packageType: title,
});
}
return s__('PackageRegistry|There are no packages yet');
},
checkDeleteAlert() {
const urlParams = new URLSearchParams(window.location.search);
const showAlert = urlParams.get(SHOW_DELETE_SUCCESS_ALERT);
......@@ -92,23 +75,13 @@ export default {
<template>
<div>
<package-title :package-help-url="packageHelpUrl" :packages-count="packagesCount" />
<package-search @sort:changed="requestPackagesList" @filter:changed="requestPackagesList" />
<gl-tabs @input="tabChanged">
<template #tabs-end>
<div
class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end"
>
<package-filter class="gl-mr-2" @filter="requestPackagesList" />
<package-sort @sort:changed="requestPackagesList" />
</div>
</template>
<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">
<gl-empty-state :title="emptyStateTitle" :svg-path="emptyListIllustration">
<template #description>
<gl-sprintf v-if="filterQuery" :message="$options.i18n.widenFilters" />
<gl-sprintf v-if="!emptySearch" :message="$options.i18n.widenFilters" />
<gl-sprintf v-else :message="$options.i18n.noResults">
<template #noPackagesLink="{ content }">
<gl-link :href="emptyListHelpUrl" target="_blank">{{ content }}</gl-link>
......@@ -118,7 +91,5 @@ export default {
</gl-empty-state>
</template>
</package-list>
</gl-tab>
</gl-tabs>
</div>
</template>
<script>
import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
import { PACKAGE_TYPES } from '../../constants';
export default {
components: {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
},
PACKAGE_TYPES,
};
</script>
<template>
<gl-filtered-search-token v-bind="{ ...$attrs }" v-on="$listeners">
<template #suggestions>
<gl-filtered-search-suggestion
v-for="(type, index) in $options.PACKAGE_TYPES"
:key="index"
:value="type.type"
>
{{ type.title }}
</gl-filtered-search-suggestion>
</template>
</gl-filtered-search-token>
</template>
......@@ -55,11 +55,7 @@ export const SORT_FIELDS = [
},
];
export const PACKAGE_REGISTRY_TABS = [
{
title: __('All'),
type: null,
},
export const PACKAGE_TYPES = [
{
title: s__('PackageRegistry|Composer'),
type: PackageType.COMPOSER,
......
......@@ -15,7 +15,6 @@ import { getNewPaginationPage } from '../utils';
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 setFilter = ({ commit }, data) => commit(types.SET_FILTER, data);
export const receivePackagesListSuccess = ({ commit }, { data, headers }) => {
......@@ -29,9 +28,9 @@ export const requestPackagesList = ({ dispatch, state }, params = {}) => {
const { page = DEFAULT_PAGE, per_page = DEFAULT_PAGE_SIZE } = params;
const { sort, orderBy } = state.sorting;
const type = state.selectedType?.type?.toLowerCase();
const nameFilter = state.filterQuery?.toLowerCase();
const packageFilters = { package_type: type, package_name: nameFilter };
const type = state.filter.find((f) => f.type === 'type');
const name = state.filter.find((f) => f.type === 'filtered-search-term');
const packageFilters = { package_type: type?.value?.data, package_name: name?.value?.data };
const apiMethod = state.config.isGroupPage ? 'groupPackages' : 'projectPackages';
......
......@@ -4,5 +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';
export const SET_FILTER = 'SET_FILTER';
......@@ -28,11 +28,7 @@ export default {
state.sorting = { ...state.sorting, ...sorting };
},
[types.SET_SELECTED_TYPE](state, type) {
state.selectedType = type;
},
[types.SET_FILTER](state, query) {
state.filterQuery = query;
[types.SET_FILTER](state, filter) {
state.filter = filter;
},
};
import { PACKAGE_REGISTRY_TABS } from '../constants';
export default () => ({
/**
* Determine if the component is loading data from the API
......@@ -49,9 +47,8 @@ export default () => ({
/**
* The search query that is used to filter packages by name
*/
filterQuery: '',
filter: [],
/**
* The selected TAB of the package types tabs
*/
selectedType: PACKAGE_REGISTRY_TABS[0],
});
---
title: Redesign the search UI for the package list
merge_request: 52575
author:
type: changed
......@@ -20433,9 +20433,6 @@ msgstr ""
msgid "PackageRegistry|Delete package"
msgstr ""
msgid "PackageRegistry|Filter by name"
msgstr ""
msgid "PackageRegistry|For more information on Composer packages in GitLab, %{linkStart}see the documentation.%{linkEnd}"
msgstr ""
......@@ -20529,9 +20526,6 @@ msgstr ""
msgid "PackageRegistry|Source project located at %{link}"
msgstr ""
msgid "PackageRegistry|There are no %{packageType} packages yet"
msgstr ""
msgid "PackageRegistry|There are no other versions of this package."
msgstr ""
......@@ -20547,6 +20541,9 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
msgid "PackageRegistry|Type"
msgstr ""
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`packages_filter renders 1`] = `
<gl-search-box-by-click-stub
clearable="true"
clearbuttontitle="Clear"
clearrecentsearchestext="Clear recent searches"
closebuttontitle="Close"
norecentsearchestext="You don't have any recent searches"
placeholder="Filter by name"
recentsearchesheader="Recent searches"
value=""
/>
`;
......@@ -6,23 +6,8 @@ exports[`packages_list_app renders 1`] = `
packagehelpurl="foo"
/>
<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>
<package-search-stub />
<b-tab-stub
tag="div"
title="All"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
......@@ -76,447 +61,5 @@ exports[`packages_list_app renders 1`] = `
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="Composer"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no Composer packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="Conan"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no Conan packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="Generic"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no Generic packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="Maven"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no Maven packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="NPM"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no NPM packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="NuGet"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no NuGet packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
<b-tab-stub
tag="div"
title="PyPI"
titlelinkclass="gl-tab-nav-item"
>
<template>
<div>
<section
class="row empty-state text-center"
>
<div
class="col-12"
>
<div
class="svg-250 svg-content"
>
<img
alt=""
class="gl-max-w-full"
src="helpSvg"
/>
</div>
</div>
<div
class="col-12"
>
<div
class="text-content gl-mx-auto gl-my-0 gl-p-5"
>
<h1
class="h4"
>
There are no PyPI packages yet
</h1>
<p>
Learn how to
<b-link-stub
class="gl-link"
event="click"
href="helpUrl"
routertag="a"
target="_blank"
>
publish and share your packages
</b-link-stub>
with GitLab.
</p>
<div>
<!---->
<!---->
</div>
</div>
</div>
</section>
</div>
</template>
</b-tab-stub>
</template>
<template>
<div
class="gl-display-flex gl-align-self-center gl-py-2 gl-flex-grow-1 gl-justify-content-end"
>
<package-filter-stub
class="gl-mr-2"
/>
<package-sort-stub />
</div>
</template>
</b-tabs-stub>
</div>
`;
import Vuex from 'vuex';
import { GlSearchBoxByClick } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import PackagesFilter from '~/packages/list/components/packages_filter.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('packages_filter', () => {
let wrapper;
let store;
const findGlSearchBox = () => wrapper.find(GlSearchBoxByClick);
const mountComponent = () => {
store = new Vuex.Store();
store.dispatch = jest.fn();
wrapper = shallowMount(PackagesFilter, {
localVue,
store,
});
};
beforeEach(mountComponent);
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('emits events', () => {
it('sets the filter value in the store on input', () => {
const searchString = 'foo';
findGlSearchBox().vm.$emit('input', searchString);
expect(store.dispatch).toHaveBeenCalledWith('setFilter', searchString);
});
it('emits the filter event when search box is submitted', () => {
findGlSearchBox().vm.$emit('submit');
expect(wrapper.emitted('filter')).toBeTruthy();
});
});
});
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState, GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
import * as commonUtils from '~/lib/utils/common_utils';
import createFlash from '~/flash';
import PackageListApp from '~/packages/list/components/packages_list_app.vue';
import PackageSearch from '~/packages/list/components/package_search.vue';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants';
import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages/list/constants';
......@@ -26,9 +27,9 @@ describe('packages_list_app', () => {
const emptyListHelpUrl = 'helpUrl';
const findEmptyState = () => wrapper.find(GlEmptyState);
const findListComponent = () => wrapper.find(PackageList);
const findTabComponent = (index = 0) => wrapper.findAll(GlTab).at(index);
const findPackageSearch = () => wrapper.find(PackageSearch);
const createStore = (filterQuery = '') => {
const createStore = (filter = []) => {
store = new Vuex.Store({
state: {
isLoading: false,
......@@ -38,7 +39,7 @@ describe('packages_list_app', () => {
emptyListHelpUrl,
packageHelpUrl: 'foo',
},
filterQuery,
filter,
},
});
store.dispatch = jest.fn();
......@@ -52,8 +53,6 @@ describe('packages_list_app', () => {
GlEmptyState,
GlLoadingIcon,
PackageList,
GlTab,
GlTabs,
GlSprintf,
GlLink,
},
......@@ -122,27 +121,9 @@ describe('packages_list_app', () => {
expect(store.dispatch).toHaveBeenCalledTimes(1);
});
describe('tab change', () => {
it('calls requestPackagesList when all tab is clicked', () => {
mountComponent();
findTabComponent().trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList');
});
it('calls requestPackagesList when a package type tab is clicked', () => {
mountComponent();
findTabComponent(1).trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList');
});
});
describe('filter without results', () => {
beforeEach(() => {
createStore('foo');
createStore([{ type: 'something' }]);
mountComponent();
});
......@@ -154,12 +135,28 @@ describe('packages_list_app', () => {
});
});
describe('Package Search', () => {
it('exists', () => {
mountComponent();
expect(findPackageSearch().exists()).toBe(true);
});
it.each(['sort:changed', 'filter:changed'])('on %p fetches data from the store', (event) => {
mountComponent();
findPackageSearch().vm.$emit(event);
expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList');
});
});
describe('delete alert handling', () => {
const { location } = window.location;
const search = `?${SHOW_DELETE_SUCCESS_ALERT}=true`;
beforeEach(() => {
createStore('foo');
createStore();
jest.spyOn(commonUtils, 'historyReplaceState').mockImplementation(() => {});
delete window.location;
window.location = {
......
import Vuex from 'vuex';
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import component from '~/packages/list/components/package_search.vue';
import PackageTypeToken from '~/packages/list/components/tokens/package_type_token.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Package Search', () => {
let wrapper;
let store;
let sorting;
let sortingItems;
const findPackageListSorting = () => wrapper.find(GlSorting);
const findSortingItems = () => wrapper.findAll(GlSortingItem);
const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
const createStore = (isGroupPage) => {
const state = {
config: {
isGroupPage,
},
sorting: {
orderBy: 'version',
sort: 'desc',
},
filter: [],
};
store = new Vuex.Store({
state,
});
store.dispatch = jest.fn();
};
const mountComponent = (isGroupPage = false) => {
createStore(isGroupPage);
wrapper = shallowMount(component, {
localVue,
store,
stubs: {
GlSortingItem,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('searching', () => {
it('has a filtered-search component', () => {
mountComponent();
expect(findFilteredSearch().exists()).toBe(true);
});
it('binds the correct props to filtered-search', () => {
mountComponent();
expect(findFilteredSearch().props()).toMatchObject({
value: [],
placeholder: 'Filter results',
availableTokens: wrapper.vm.tokens,
});
});
it('updates vuex when value changes', () => {
mountComponent();
findFilteredSearch().vm.$emit('input', ['foo']);
expect(store.dispatch).toHaveBeenCalledWith('setFilter', ['foo']);
});
it('emits filter:changed on submit event', () => {
mountComponent();
findFilteredSearch().vm.$emit('submit');
expect(wrapper.emitted('filter:changed')).toEqual([[]]);
});
it('emits filter:changed on clear event and reset vuex', () => {
mountComponent();
findFilteredSearch().vm.$emit('clear');
expect(store.dispatch).toHaveBeenCalledWith('setFilter', []);
expect(wrapper.emitted('filter:changed')).toEqual([[]]);
});
it('has a PackageTypeToken token', () => {
mountComponent();
expect(findFilteredSearch().props('availableTokens')).toEqual(
expect.arrayContaining([
expect.objectContaining({ token: PackageTypeToken, type: 'type', icon: 'package' }),
]),
);
});
});
describe('sorting', () => {
describe('when is in projects', () => {
beforeEach(() => {
mountComponent();
sorting = findPackageListSorting();
sortingItems = findSortingItems();
});
it('has all the sortable items', () => {
expect(sortingItems).toHaveLength(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).toHaveLength(wrapper.vm.sortableFields.length);
});
});
});
});
import Vuex from 'vuex';
import { GlSorting, GlSortingItem } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import stubChildren from 'helpers/stub_children';
import PackagesSort from '~/packages/list/components/packages_sort.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('packages_sort', () => {
let wrapper;
let store;
let sorting;
let sortingItems;
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).toHaveLength(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).toHaveLength(wrapper.vm.sortableFields.length);
});
});
});
import { shallowMount } from '@vue/test-utils';
import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
import component from '~/packages/list/components/tokens/package_type_token.vue';
import { PACKAGE_TYPES } from '~/packages/list/constants';
describe('packages_filter', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const mountComponent = ({ attrs, listeners } = {}) => {
wrapper = shallowMount(component, {
attrs,
listeners,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('it binds all of his attrs to filtered search token', () => {
mountComponent({ attrs: { foo: 'bar' } });
expect(findFilteredSearchToken().attributes('foo')).toBe('bar');
});
it('it binds all of his events to filtered search token', () => {
const clickListener = jest.fn();
mountComponent({ listeners: { click: clickListener } });
findFilteredSearchToken().vm.$emit('click');
expect(clickListener).toHaveBeenCalled();
});
it.each(PACKAGE_TYPES.map((p, index) => [p, index]))(
'displays a suggestion for %p',
(packageType, index) => {
mountComponent();
const item = findFilteredSearchSuggestions().at(index);
expect(item.text()).toBe(packageType.title);
expect(item.props('value')).toBe(packageType.type);
},
);
});
......@@ -30,11 +30,13 @@ describe('Actions Package list store', () => {
sort: 'asc',
orderBy: 'version',
};
const filter = [];
it('should fetch the project packages list when isGroupPage is false', (done) => {
testAction(
actions.requestPackagesList,
undefined,
{ config: { isGroupPage: false, resourceId: 1 }, sorting },
{ config: { isGroupPage: false, resourceId: 1 }, sorting, filter },
[],
[
{ type: 'setLoading', payload: true },
......@@ -54,7 +56,7 @@ describe('Actions Package list store', () => {
testAction(
actions.requestPackagesList,
undefined,
{ config: { isGroupPage: true, resourceId: 2 }, sorting },
{ config: { isGroupPage: true, resourceId: 2 }, sorting, filter },
[],
[
{ type: 'setLoading', payload: true },
......@@ -70,7 +72,7 @@ describe('Actions Package list store', () => {
);
});
it('should fetch packages of a certain type when selectedType is present', (done) => {
it('should fetch packages of a certain type when a filter with a type is present', (done) => {
const packageType = 'maven';
testAction(
......@@ -79,7 +81,7 @@ describe('Actions Package list store', () => {
{
config: { isGroupPage: false, resourceId: 1 },
sorting,
selectedType: { type: packageType },
filter: [{ type: 'type', value: { data: 'maven' } }],
},
[],
[
......@@ -107,7 +109,7 @@ describe('Actions Package list store', () => {
testAction(
actions.requestPackagesList,
undefined,
{ config: { isGroupPage: false, resourceId: 2 }, sorting },
{ config: { isGroupPage: false, resourceId: 2 }, sorting, filter },
[],
[
{ type: 'setLoading', payload: true },
......
......@@ -78,17 +78,10 @@ describe('Mutations Registry Store', () => {
});
});
describe('SET_SELECTED_TYPE', () => {
it('should set the selected type', () => {
mutations[types.SET_SELECTED_TYPE](mockState, { type: 'maven' });
expect(mockState.selectedType).toEqual({ type: 'maven' });
});
});
describe('SET_FILTER', () => {
it('should set the filter query', () => {
mutations[types.SET_FILTER](mockState, 'foo');
expect(mockState.filterQuery).toEqual('foo');
expect(mockState.filter).toEqual('foo');
});
});
});
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