diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 13:18:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-09-20 13:18:24 +0000 |
commit | 0653e08efd039a5905f3fa4f6e9cef9f5d2f799c (patch) | |
tree | 4dcc884cf6d81db44adae4aa99f8ec1233a41f55 /spec/frontend/packages_and_registries/package_registry/components/list | |
parent | 744144d28e3e7fddc117924fef88de5d9674fe4c (diff) | |
download | gitlab-ce-0653e08efd039a5905f3fa4f6e9cef9f5d2f799c.tar.gz |
Add latest changes from gitlab-org/gitlab@14-3-stable-eev14.3.0-rc42
Diffstat (limited to 'spec/frontend/packages_and_registries/package_registry/components/list')
6 files changed, 805 insertions, 0 deletions
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/packages_list_app_spec.js.snap new file mode 100644 index 00000000000..dbebdeeb452 --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/packages_list_app_spec.js.snap @@ -0,0 +1,68 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`packages_list_app renders 1`] = ` +<div> + <div + help-url="foo" + /> + + <div /> + + <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" + role="img" + 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 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 + class="gl-display-flex gl-flex-wrap gl-justify-content-center" + > + <!----> + + <!----> + </div> + </div> + </div> + </section> + </div> +</div> +`; diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_app_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_app_spec.js new file mode 100644 index 00000000000..6c871a34d50 --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_app_spec.js @@ -0,0 +1,273 @@ +import { GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import setWindowLocation from 'helpers/set_window_location_helper'; +import createFlash from '~/flash'; +import * as commonUtils from '~/lib/utils/common_utils'; +import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages/list/constants'; +import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants'; +import PackageListApp from '~/packages_and_registries/package_registry/components/list/packages_list_app.vue'; +import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants'; +import * as packageUtils from '~/packages_and_registries/shared/utils'; + +jest.mock('~/lib/utils/common_utils'); +jest.mock('~/flash'); + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('packages_list_app', () => { + let wrapper; + let store; + + const PackageList = { + name: 'package-list', + template: '<div><slot name="empty-state"></slot></div>', + }; + const GlLoadingIcon = { name: 'gl-loading-icon', template: '<div>loading</div>' }; + + // we need to manually stub dynamic imported components because shallowMount is not able to stub them automatically. See: https://github.com/vuejs/vue-test-utils/issues/1279 + const PackageSearch = { name: 'PackageSearch', template: '<div></div>' }; + const PackageTitle = { name: 'PackageTitle', template: '<div></div>' }; + const InfrastructureTitle = { name: 'InfrastructureTitle', template: '<div></div>' }; + const InfrastructureSearch = { name: 'InfrastructureSearch', template: '<div></div>' }; + + const emptyListHelpUrl = 'helpUrl'; + const findEmptyState = () => wrapper.find(GlEmptyState); + const findListComponent = () => wrapper.find(PackageList); + const findPackageSearch = () => wrapper.find(PackageSearch); + const findPackageTitle = () => wrapper.find(PackageTitle); + const findInfrastructureTitle = () => wrapper.find(InfrastructureTitle); + const findInfrastructureSearch = () => wrapper.find(InfrastructureSearch); + + const createStore = (filter = []) => { + store = new Vuex.Store({ + state: { + isLoading: false, + config: { + resourceId: 'project_id', + emptyListIllustration: 'helpSvg', + emptyListHelpUrl, + packageHelpUrl: 'foo', + }, + filter, + }, + }); + store.dispatch = jest.fn(); + }; + + const mountComponent = (provide) => { + wrapper = shallowMount(PackageListApp, { + localVue, + store, + stubs: { + GlEmptyState, + GlLoadingIcon, + PackageList, + GlSprintf, + GlLink, + PackageSearch, + PackageTitle, + InfrastructureTitle, + InfrastructureSearch, + }, + provide, + }); + }; + + beforeEach(() => { + createStore(); + jest.spyOn(packageUtils, 'getQueryParams').mockReturnValue({}); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders', () => { + mountComponent(); + expect(wrapper.element).toMatchSnapshot(); + }); + + it('call requestPackagesList on page:changed', () => { + mountComponent(); + store.dispatch.mockClear(); + + const list = findListComponent(); + list.vm.$emit('page:changed', 1); + expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList', { page: 1 }); + }); + + it('call requestDeletePackage on package:delete', () => { + mountComponent(); + + const list = findListComponent(); + list.vm.$emit('package:delete', 'foo'); + expect(store.dispatch).toHaveBeenCalledWith('requestDeletePackage', 'foo'); + }); + + it('does call requestPackagesList only one time on render', () => { + mountComponent(); + + expect(store.dispatch).toHaveBeenCalledTimes(3); + expect(store.dispatch).toHaveBeenNthCalledWith(1, 'setSorting', expect.any(Object)); + expect(store.dispatch).toHaveBeenNthCalledWith(2, 'setFilter', expect.any(Array)); + expect(store.dispatch).toHaveBeenNthCalledWith(3, 'requestPackagesList'); + }); + + describe('url query string handling', () => { + const defaultQueryParamsMock = { + search: [1, 2], + type: 'npm', + sort: 'asc', + orderBy: 'created', + }; + + it('calls setSorting with the query string based sorting', () => { + jest.spyOn(packageUtils, 'getQueryParams').mockReturnValue(defaultQueryParamsMock); + + mountComponent(); + + expect(store.dispatch).toHaveBeenNthCalledWith(1, 'setSorting', { + orderBy: defaultQueryParamsMock.orderBy, + sort: defaultQueryParamsMock.sort, + }); + }); + + it('calls setFilter with the query string based filters', () => { + jest.spyOn(packageUtils, 'getQueryParams').mockReturnValue(defaultQueryParamsMock); + + mountComponent(); + + expect(store.dispatch).toHaveBeenNthCalledWith(2, 'setFilter', [ + { type: 'type', value: { data: defaultQueryParamsMock.type } }, + { type: FILTERED_SEARCH_TERM, value: { data: defaultQueryParamsMock.search[0] } }, + { type: FILTERED_SEARCH_TERM, value: { data: defaultQueryParamsMock.search[1] } }, + ]); + }); + + it('calls setSorting and setFilters with the results of extractFilterAndSorting', () => { + jest + .spyOn(packageUtils, 'extractFilterAndSorting') + .mockReturnValue({ filters: ['foo'], sorting: { sort: 'desc' } }); + + mountComponent(); + + expect(store.dispatch).toHaveBeenNthCalledWith(1, 'setSorting', { sort: 'desc' }); + expect(store.dispatch).toHaveBeenNthCalledWith(2, 'setFilter', ['foo']); + }); + }); + + describe('empty state', () => { + it('generate the correct empty list link', () => { + mountComponent(); + + const link = findListComponent().find(GlLink); + + expect(link.attributes('href')).toBe(emptyListHelpUrl); + expect(link.text()).toBe('publish and share your packages'); + }); + + it('includes the right content on the default tab', () => { + mountComponent(); + + const heading = findEmptyState().find('h1'); + + expect(heading.text()).toBe('There are no packages yet'); + }); + }); + + describe('filter without results', () => { + beforeEach(() => { + createStore([{ type: 'something' }]); + mountComponent(); + }); + + it('should show specific empty message', () => { + expect(findEmptyState().text()).toContain('Sorry, your filter produced no results'); + expect(findEmptyState().text()).toContain( + 'To widen your search, change or remove the filters above', + ); + }); + }); + + describe('Package Search', () => { + it('exists', () => { + mountComponent(); + + expect(findPackageSearch().exists()).toBe(true); + }); + + it('on update fetches data from the store', () => { + mountComponent(); + store.dispatch.mockClear(); + + findPackageSearch().vm.$emit('update'); + + expect(store.dispatch).toHaveBeenCalledWith('requestPackagesList'); + }); + }); + + describe('Infrastructure config', () => { + it('defaults to package registry components', () => { + mountComponent(); + + expect(findPackageSearch().exists()).toBe(true); + expect(findPackageTitle().exists()).toBe(true); + + expect(findInfrastructureTitle().exists()).toBe(false); + expect(findInfrastructureSearch().exists()).toBe(false); + }); + + it('mount different component based on the provided values', () => { + mountComponent({ + titleComponent: 'InfrastructureTitle', + searchComponent: 'InfrastructureSearch', + }); + + expect(findPackageSearch().exists()).toBe(false); + expect(findPackageTitle().exists()).toBe(false); + + expect(findInfrastructureTitle().exists()).toBe(true); + expect(findInfrastructureSearch().exists()).toBe(true); + }); + }); + + describe('delete alert handling', () => { + const originalLocation = window.location.href; + const search = `?${SHOW_DELETE_SUCCESS_ALERT}=true`; + + beforeEach(() => { + createStore(); + jest.spyOn(commonUtils, 'historyReplaceState').mockImplementation(() => {}); + setWindowLocation(search); + }); + + afterEach(() => { + setWindowLocation(originalLocation); + }); + + it(`creates a flash if the query string contains ${SHOW_DELETE_SUCCESS_ALERT}`, () => { + mountComponent(); + + expect(createFlash).toHaveBeenCalledWith({ + message: DELETE_PACKAGE_SUCCESS_MESSAGE, + type: 'notice', + }); + }); + + it('calls historyReplaceState with a clean url', () => { + mountComponent(); + + expect(commonUtils.historyReplaceState).toHaveBeenCalledWith(originalLocation); + }); + + it(`does nothing if the query string does not contain ${SHOW_DELETE_SUCCESS_ALERT}`, () => { + setWindowLocation('?'); + mountComponent(); + + expect(createFlash).not.toHaveBeenCalled(); + expect(commonUtils.historyReplaceState).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js new file mode 100644 index 00000000000..b624e66482d --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js @@ -0,0 +1,217 @@ +import { GlTable, GlPagination, GlModal } from '@gitlab/ui'; +import { mount, createLocalVue } from '@vue/test-utils'; +import { last } from 'lodash'; +import Vuex from 'vuex'; +import stubChildren from 'helpers/stub_children'; +import { packageList } from 'jest/packages/mock_data'; +import PackagesListRow from '~/packages/shared/components/package_list_row.vue'; +import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue'; +import { TrackingActions } from '~/packages/shared/constants'; +import * as SharedUtils from '~/packages/shared/utils'; +import PackagesList from '~/packages_and_registries/package_registry/components/list/packages_list.vue'; +import Tracking from '~/tracking'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('packages_list', () => { + let wrapper; + let store; + + const EmptySlotStub = { name: 'empty-slot-stub', template: '<div>bar</div>' }; + + const findPackagesListLoader = () => wrapper.find(PackagesListLoader); + const findPackageListPagination = () => wrapper.find(GlPagination); + const findPackageListDeleteModal = () => wrapper.find(GlModal); + const findEmptySlot = () => wrapper.find(EmptySlotStub); + const findPackagesListRow = () => wrapper.find(PackagesListRow); + + const createStore = (isGroupPage, packages, isLoading) => { + const state = { + isLoading, + packages, + pagination: { + perPage: 1, + total: 1, + page: 1, + }, + config: { + isGroupPage, + }, + sorting: { + orderBy: 'version', + sort: 'desc', + }, + }; + store = new Vuex.Store({ + state, + getters: { + getList: () => packages, + }, + }); + store.dispatch = jest.fn(); + }; + + const mountComponent = ({ + isGroupPage = false, + packages = packageList, + isLoading = false, + ...options + } = {}) => { + createStore(isGroupPage, packages, isLoading); + + wrapper = mount(PackagesList, { + localVue, + store, + stubs: { + ...stubChildren(PackagesList), + GlTable, + GlModal, + }, + ...options, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('when is loading', () => { + beforeEach(() => { + mountComponent({ + packages: [], + isLoading: true, + }); + }); + + it('shows skeleton loader when loading', () => { + expect(findPackagesListLoader().exists()).toBe(true); + }); + }); + + describe('when is not loading', () => { + beforeEach(() => { + mountComponent(); + }); + + it('does not show skeleton loader when not loading', () => { + expect(findPackagesListLoader().exists()).toBe(false); + }); + }); + + describe('layout', () => { + beforeEach(() => { + mountComponent(); + }); + + it('contains a pagination component', () => { + const sorting = findPackageListPagination(); + expect(sorting.exists()).toBe(true); + }); + + it('contains a modal component', () => { + const sorting = findPackageListDeleteModal(); + expect(sorting.exists()).toBe(true); + }); + }); + + describe('when the user can destroy the package', () => { + beforeEach(() => { + mountComponent(); + }); + + it('setItemToBeDeleted sets itemToBeDeleted and open the modal', () => { + const mockModalShow = jest.spyOn(wrapper.vm.$refs.packageListDeleteModal, 'show'); + const item = last(wrapper.vm.list); + + findPackagesListRow().vm.$emit('packageToDelete', item); + + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.vm.itemToBeDeleted).toEqual(item); + expect(mockModalShow).toHaveBeenCalled(); + }); + }); + + it('deleteItemConfirmation resets itemToBeDeleted', () => { + wrapper.setData({ itemToBeDeleted: 1 }); + wrapper.vm.deleteItemConfirmation(); + expect(wrapper.vm.itemToBeDeleted).toEqual(null); + }); + + it('deleteItemConfirmation emit package:delete', () => { + const itemToBeDeleted = { id: 2 }; + wrapper.setData({ itemToBeDeleted }); + wrapper.vm.deleteItemConfirmation(); + return wrapper.vm.$nextTick(() => { + expect(wrapper.emitted('package:delete')[0]).toEqual([itemToBeDeleted]); + }); + }); + + it('deleteItemCanceled resets itemToBeDeleted', () => { + wrapper.setData({ itemToBeDeleted: 1 }); + wrapper.vm.deleteItemCanceled(); + expect(wrapper.vm.itemToBeDeleted).toEqual(null); + }); + }); + + describe('when the list is empty', () => { + beforeEach(() => { + mountComponent({ + packages: [], + slots: { + 'empty-state': EmptySlotStub, + }, + }); + }); + + it('show the empty slot', () => { + const emptySlot = findEmptySlot(); + expect(emptySlot.exists()).toBe(true); + }); + }); + + describe('pagination component', () => { + let pagination; + let modelEvent; + + beforeEach(() => { + mountComponent(); + pagination = findPackageListPagination(); + // retrieve the event used by v-model, a more sturdy approach than hardcoding it + modelEvent = pagination.vm.$options.model.event; + }); + + it('emits page:changed events when the page changes', () => { + pagination.vm.$emit(modelEvent, 2); + expect(wrapper.emitted('page:changed')).toEqual([[2]]); + }); + }); + + describe('tracking', () => { + let eventSpy; + let utilSpy; + const category = 'foo'; + + beforeEach(() => { + mountComponent(); + eventSpy = jest.spyOn(Tracking, 'event'); + utilSpy = jest.spyOn(SharedUtils, 'packageTypeToTrackCategory').mockReturnValue(category); + wrapper.setData({ itemToBeDeleted: { package_type: 'conan' } }); + }); + + it('tracking category calls packageTypeToTrackCategory', () => { + expect(wrapper.vm.tracking.category).toBe(category); + expect(utilSpy).toHaveBeenCalledWith('conan'); + }); + + it('deleteItemConfirmation calls event', () => { + wrapper.vm.deleteItemConfirmation(); + expect(eventSpy).toHaveBeenCalledWith( + category, + TrackingActions.DELETE_PACKAGE, + expect.any(Object), + ); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js new file mode 100644 index 00000000000..42bc9fa3a9e --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_search_spec.js @@ -0,0 +1,128 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { sortableFields } from '~/packages/list/utils'; +import component from '~/packages_and_registries/package_registry/components/list/package_search.vue'; +import PackageTypeToken from '~/packages_and_registries/package_registry/components/list/tokens/package_type_token.vue'; +import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue'; +import UrlSync from '~/vue_shared/components/url_sync.vue'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('Package Search', () => { + let wrapper; + let store; + + const findRegistrySearch = () => wrapper.findComponent(RegistrySearch); + const findUrlSync = () => wrapper.findComponent(UrlSync); + + 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: { + UrlSync, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('has a registry search component', () => { + mountComponent(); + + expect(findRegistrySearch().exists()).toBe(true); + expect(findRegistrySearch().props()).toMatchObject({ + filter: store.state.filter, + sorting: store.state.sorting, + tokens: expect.arrayContaining([ + expect.objectContaining({ token: PackageTypeToken, type: 'type', icon: 'package' }), + ]), + sortableFields: sortableFields(), + }); + }); + + it.each` + isGroupPage | page + ${false} | ${'project'} + ${true} | ${'group'} + `('in a $page page binds the right props', ({ isGroupPage }) => { + mountComponent(isGroupPage); + + expect(findRegistrySearch().props()).toMatchObject({ + filter: store.state.filter, + sorting: store.state.sorting, + tokens: expect.arrayContaining([ + expect.objectContaining({ token: PackageTypeToken, type: 'type', icon: 'package' }), + ]), + sortableFields: sortableFields(isGroupPage), + }); + }); + + it('on sorting:changed emits update event and calls vuex setSorting', () => { + const payload = { sort: 'foo' }; + + mountComponent(); + + findRegistrySearch().vm.$emit('sorting:changed', payload); + + expect(store.dispatch).toHaveBeenCalledWith('setSorting', payload); + expect(wrapper.emitted('update')).toEqual([[]]); + }); + + it('on filter:changed calls vuex setFilter', () => { + const payload = ['foo']; + + mountComponent(); + + findRegistrySearch().vm.$emit('filter:changed', payload); + + expect(store.dispatch).toHaveBeenCalledWith('setFilter', payload); + }); + + it('on filter:submit emits update event', () => { + mountComponent(); + + findRegistrySearch().vm.$emit('filter:submit'); + + expect(wrapper.emitted('update')).toEqual([[]]); + }); + + it('has a UrlSync component', () => { + mountComponent(); + + expect(findUrlSync().exists()).toBe(true); + }); + + it('on query:changed calls updateQuery from UrlSync', () => { + jest.spyOn(UrlSync.methods, 'updateQuery').mockImplementation(() => {}); + + mountComponent(); + + findRegistrySearch().vm.$emit('query:changed'); + + expect(UrlSync.methods.updateQuery).toHaveBeenCalled(); + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js new file mode 100644 index 00000000000..3fa96ce1d29 --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js @@ -0,0 +1,71 @@ +import { shallowMount } from '@vue/test-utils'; +import { LIST_INTRO_TEXT, LIST_TITLE_TEXT } from '~/packages/list/constants'; +import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue'; +import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; +import TitleArea from '~/vue_shared/components/registry/title_area.vue'; + +describe('PackageTitle', () => { + let wrapper; + let store; + + const findTitleArea = () => wrapper.find(TitleArea); + const findMetadataItem = () => wrapper.find(MetadataItem); + + const mountComponent = (propsData = { helpUrl: 'foo' }) => { + wrapper = shallowMount(PackageTitle, { + store, + propsData, + stubs: { + TitleArea, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('title area', () => { + it('exists', () => { + mountComponent(); + + expect(findTitleArea().exists()).toBe(true); + }); + + it('has the correct props', () => { + mountComponent(); + + expect(findTitleArea().props()).toMatchObject({ + title: LIST_TITLE_TEXT, + infoMessages: [{ text: LIST_INTRO_TEXT, link: 'foo' }], + }); + }); + }); + + describe.each` + count | exist | text + ${null} | ${false} | ${''} + ${undefined} | ${false} | ${''} + ${0} | ${true} | ${'0 Packages'} + ${1} | ${true} | ${'1 Package'} + ${2} | ${true} | ${'2 Packages'} + `('when count is $count metadata item', ({ count, exist, text }) => { + beforeEach(() => { + mountComponent({ count, helpUrl: 'foo' }); + }); + + it(`is ${exist} that it exists`, () => { + expect(findMetadataItem().exists()).toBe(exist); + }); + + if (exist) { + it('has the correct props', () => { + expect(findMetadataItem().props()).toMatchObject({ + icon: 'package', + text, + }); + }); + } + }); +}); diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js new file mode 100644 index 00000000000..b0cbe34f0b9 --- /dev/null +++ b/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js @@ -0,0 +1,48 @@ +import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +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); + }, + ); +}); |