diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /spec/frontend/packages | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'spec/frontend/packages')
11 files changed, 250 insertions, 59 deletions
diff --git a/spec/frontend/packages/details/store/getters_spec.js b/spec/frontend/packages/details/store/getters_spec.js index f12b75d3b70..005adece56e 100644 --- a/spec/frontend/packages/details/store/getters_spec.js +++ b/spec/frontend/packages/details/store/getters_spec.js @@ -27,6 +27,7 @@ import { mockPipelineInfo, mavenPackage as packageWithoutBuildInfo, pypiPackage, + rubygemsPackage, } from '../../mock_data'; import { generateMavenCommand, @@ -104,6 +105,7 @@ describe('Getters PackageDetails Store', () => { ${npmPackage} | ${'npm'} ${nugetPackage} | ${'NuGet'} ${pypiPackage} | ${'PyPI'} + ${rubygemsPackage} | ${'RubyGems'} `(`package type`, ({ packageEntity, expectedResult }) => { beforeEach(() => setupState({ packageEntity })); diff --git a/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap index 3f17731584c..07aba62fef6 100644 --- a/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap +++ b/spec/frontend/packages/list/components/__snapshots__/packages_list_app_spec.js.snap @@ -2,11 +2,11 @@ exports[`packages_list_app renders 1`] = ` <div> - <package-title-stub - packagehelpurl="foo" + <div + help-url="foo" /> - <package-search-stub /> + <div /> <div> <section @@ -52,7 +52,9 @@ exports[`packages_list_app renders 1`] = ` with GitLab. </p> - <div> + <div + class="gl-display-flex gl-flex-wrap gl-justify-content-center" + > <!----> <!----> diff --git a/spec/frontend/packages/list/components/packages_list_app_spec.js b/spec/frontend/packages/list/components/packages_list_app_spec.js index 6862d23c4ff..4de2dd0789e 100644 --- a/spec/frontend/packages/list/components/packages_list_app_spec.js +++ b/spec/frontend/packages/list/components/packages_list_app_spec.js @@ -3,10 +3,11 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import createFlash from '~/flash'; import * as commonUtils from '~/lib/utils/common_utils'; -import PackageSearch from '~/packages/list/components/package_search.vue'; import PackageListApp from '~/packages/list/components/packages_list_app.vue'; import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages/list/constants'; import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages/shared/constants'; +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'); @@ -24,10 +25,19 @@ describe('packages_list_app', () => { }; 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({ @@ -45,7 +55,7 @@ describe('packages_list_app', () => { store.dispatch = jest.fn(); }; - const mountComponent = () => { + const mountComponent = (provide) => { wrapper = shallowMount(PackageListApp, { localVue, store, @@ -55,12 +65,18 @@ describe('packages_list_app', () => { PackageList, GlSprintf, GlLink, + PackageSearch, + PackageTitle, + InfrastructureTitle, + InfrastructureSearch, }, + provide, }); }; beforeEach(() => { createStore(); + jest.spyOn(packageUtils, 'getQueryParams').mockReturnValue({}); }); afterEach(() => { @@ -72,25 +88,6 @@ describe('packages_list_app', () => { expect(wrapper.element).toMatchSnapshot(); }); - 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'); - }); - }); - it('call requestPackagesList on page:changed', () => { mountComponent(); store.dispatch.mockClear(); @@ -108,10 +105,75 @@ describe('packages_list_app', () => { expect(store.dispatch).toHaveBeenCalledWith('requestDeletePackage', 'foo'); }); - it('does not call requestPackagesList two times on render', () => { + it('does call requestPackagesList only one time on render', () => { mountComponent(); - expect(store.dispatch).toHaveBeenCalledTimes(1); + 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', () => { @@ -145,6 +207,31 @@ describe('packages_list_app', () => { }); }); + 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 { location } = window.location; const search = `?${SHOW_DELETE_SUCCESS_ALERT}=true`; diff --git a/spec/frontend/packages/list/components/packages_search_spec.js b/spec/frontend/packages/list/components/packages_search_spec.js index 9b62dde8d2b..30fad74b493 100644 --- a/spec/frontend/packages/list/components/packages_search_spec.js +++ b/spec/frontend/packages/list/components/packages_search_spec.js @@ -2,8 +2,9 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; import component from '~/packages/list/components/package_search.vue'; import PackageTypeToken from '~/packages/list/components/tokens/package_type_token.vue'; -import getTableHeaders from '~/packages/list/utils'; +import { sortableFields } from '~/packages/list/utils'; 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); @@ -12,7 +13,8 @@ describe('Package Search', () => { let wrapper; let store; - const findRegistrySearch = () => wrapper.find(RegistrySearch); + const findRegistrySearch = () => wrapper.findComponent(RegistrySearch); + const findUrlSync = () => wrapper.findComponent(UrlSync); const createStore = (isGroupPage) => { const state = { @@ -37,6 +39,9 @@ describe('Package Search', () => { wrapper = shallowMount(component, { localVue, store, + stubs: { + UrlSync, + }, }); }; @@ -55,7 +60,7 @@ describe('Package Search', () => { tokens: expect.arrayContaining([ expect.objectContaining({ token: PackageTypeToken, type: 'type', icon: 'package' }), ]), - sortableFields: getTableHeaders(), + sortableFields: sortableFields(), }); }); @@ -72,7 +77,7 @@ describe('Package Search', () => { tokens: expect.arrayContaining([ expect.objectContaining({ token: PackageTypeToken, type: 'type', icon: 'package' }), ]), - sortableFields: getTableHeaders(isGroupPage), + sortableFields: sortableFields(isGroupPage), }); }); @@ -104,4 +109,20 @@ describe('Package Search', () => { 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/list/components/packages_title_spec.js b/spec/frontend/packages/list/components/packages_title_spec.js index 3716e8daa7c..a17f72e3133 100644 --- a/spec/frontend/packages/list/components/packages_title_spec.js +++ b/spec/frontend/packages/list/components/packages_title_spec.js @@ -11,7 +11,7 @@ describe('PackageTitle', () => { const findTitleArea = () => wrapper.find(TitleArea); const findMetadataItem = () => wrapper.find(MetadataItem); - const mountComponent = (propsData = { packageHelpUrl: 'foo' }) => { + const mountComponent = (propsData = { helpUrl: 'foo' }) => { wrapper = shallowMount(PackageTitle, { store, propsData, @@ -44,15 +44,15 @@ describe('PackageTitle', () => { }); describe.each` - packagesCount | exist | text - ${null} | ${false} | ${''} - ${undefined} | ${false} | ${''} - ${0} | ${true} | ${'0 Packages'} - ${1} | ${true} | ${'1 Package'} - ${2} | ${true} | ${'2 Packages'} - `('when packagesCount is $packagesCount metadata item', ({ packagesCount, exist, text }) => { + 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({ packagesCount, packageHelpUrl: 'foo' }); + mountComponent({ count, helpUrl: 'foo' }); }); it(`is ${exist} that it exists`, () => { diff --git a/spec/frontend/packages/list/utils_spec.js b/spec/frontend/packages/list/utils_spec.js index 5bcc3784752..4e4f7b8a723 100644 --- a/spec/frontend/packages/list/utils_spec.js +++ b/spec/frontend/packages/list/utils_spec.js @@ -1,6 +1,15 @@ -import { getNewPaginationPage } from '~/packages/list/utils'; +import { SORT_FIELDS } from '~/packages/list/constants'; +import { getNewPaginationPage, sortableFields } from '~/packages/list/utils'; describe('Packages list utils', () => { + describe('sortableFields', () => { + it('returns the correct list when is a project page', () => { + expect(sortableFields()).toEqual(SORT_FIELDS.filter((f) => f.orderBy !== 'project_path')); + }); + it('returns the full list on the group page', () => { + expect(sortableFields(true)).toEqual(SORT_FIELDS); + }); + }); describe('packageTypeDisplay', () => { it('returns the current page when total items exceeds pagniation', () => { expect(getNewPaginationPage(2, 20, 21)).toBe(2); diff --git a/spec/frontend/packages/mock_data.js b/spec/frontend/packages/mock_data.js index fbc167729d9..06009daba54 100644 --- a/spec/frontend/packages/mock_data.js +++ b/spec/frontend/packages/mock_data.js @@ -134,6 +134,23 @@ export const nugetPackage = { }, }; +export const rubygemsPackage = { + created_at: '2015-12-10', + id: 4, + name: 'RubyGem1', + package_files: [], + package_type: 'rubygems', + project_id: 1, + tags: [], + updated_at: '2015-12-10', + version: '1.0.0', + rubygems_metadatum: { + author: 'Fake Name', + summary: 'My gem', + email: 'tanuki@fake.com', + }, +}; + export const pypiPackage = { created_at: '2015-12-10', id: 5, diff --git a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap index 77095f7c611..03b98478f3e 100644 --- a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap +++ b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap @@ -51,20 +51,7 @@ exports[`packages_list_row renders 1`] = ` <!----> - <div - class="d-flex align-items-center" - data-testid="package-type" - > - <gl-icon-stub - class="gl-ml-3 gl-mr-2" - name="package" - size="16" - /> - - <span> - Maven - </span> - </div> + <div /> <package-path-stub path="foo/bar/baz" diff --git a/spec/frontend/packages/shared/components/package_icon_and_name_spec.js b/spec/frontend/packages/shared/components/package_icon_and_name_spec.js new file mode 100644 index 00000000000..c96a570a29c --- /dev/null +++ b/spec/frontend/packages/shared/components/package_icon_and_name_spec.js @@ -0,0 +1,32 @@ +import { GlIcon } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import PackageIconAndName from '~/packages/shared/components/package_icon_and_name.vue'; + +describe('PackageIconAndName', () => { + let wrapper; + + const findIcon = () => wrapper.find(GlIcon); + + const mountComponent = () => { + wrapper = shallowMount(PackageIconAndName, { + slots: { + default: 'test', + }, + }); + }; + + it('has an icon', () => { + mountComponent(); + + const icon = findIcon(); + + expect(icon.exists()).toBe(true); + expect(icon.props('name')).toBe('package'); + }); + + it('renders the slot content', () => { + mountComponent(); + + expect(wrapper.text()).toBe('test'); + }); +}); diff --git a/spec/frontend/packages/shared/components/package_list_row_spec.js b/spec/frontend/packages/shared/components/package_list_row_spec.js index 1c0ef7e3539..fd54cd0f25d 100644 --- a/spec/frontend/packages/shared/components/package_list_row_spec.js +++ b/spec/frontend/packages/shared/components/package_list_row_spec.js @@ -1,7 +1,9 @@ import { shallowMount } from '@vue/test-utils'; + import PackagesListRow from '~/packages/shared/components/package_list_row.vue'; import PackagePath from '~/packages/shared/components/package_path.vue'; import PackageTags from '~/packages/shared/components/package_tags.vue'; + import ListItem from '~/vue_shared/components/registry/list_item.vue'; import { packageList } from '../../mock_data'; @@ -11,20 +13,30 @@ describe('packages_list_row', () => { const [packageWithoutTags, packageWithTags] = packageList; + const InfrastructureIconAndName = { name: 'InfrastructureIconAndName', template: '<div></div>' }; + const PackageIconAndName = { name: 'PackageIconAndName', template: '<div></div>' }; + const findPackageTags = () => wrapper.find(PackageTags); const findPackagePath = () => wrapper.find(PackagePath); const findDeleteButton = () => wrapper.find('[data-testid="action-delete"]'); - const findPackageType = () => wrapper.find('[data-testid="package-type"]'); + const findPackageIconAndName = () => wrapper.find(PackageIconAndName); + const findInfrastructureIconAndName = () => wrapper.find(InfrastructureIconAndName); const mountComponent = ({ isGroup = false, packageEntity = packageWithoutTags, showPackageType = true, disableDelete = false, + provide, } = {}) => { wrapper = shallowMount(PackagesListRow, { store, - stubs: { ListItem }, + provide, + stubs: { + ListItem, + InfrastructureIconAndName, + PackageIconAndName, + }, propsData: { packageLink: 'foo', packageEntity, @@ -72,13 +84,13 @@ describe('packages_list_row', () => { it('shows the type when set', () => { mountComponent(); - expect(findPackageType().exists()).toBe(true); + expect(findPackageIconAndName().exists()).toBe(true); }); it('does not show the type when not set', () => { mountComponent({ showPackageType: false }); - expect(findPackageType().exists()).toBe(false); + expect(findPackageIconAndName().exists()).toBe(false); }); }); @@ -113,4 +125,25 @@ describe('packages_list_row', () => { expect(wrapper.emitted('packageToDelete')[0]).toEqual([packageWithoutTags]); }); }); + + describe('Infrastructure config', () => { + it('defaults to package registry components', () => { + mountComponent(); + + expect(findPackageIconAndName().exists()).toBe(true); + expect(findInfrastructureIconAndName().exists()).toBe(false); + }); + + it('mounts different component based on the provided values', () => { + mountComponent({ + provide: { + iconComponent: 'InfrastructureIconAndName', + }, + }); + + expect(findPackageIconAndName().exists()).toBe(false); + + expect(findInfrastructureIconAndName().exists()).toBe(true); + }); + }); }); diff --git a/spec/frontend/packages/shared/utils_spec.js b/spec/frontend/packages/shared/utils_spec.js index 4a95def1bef..463e4a4febb 100644 --- a/spec/frontend/packages/shared/utils_spec.js +++ b/spec/frontend/packages/shared/utils_spec.js @@ -38,6 +38,7 @@ describe('Packages shared utils', () => { ${'npm'} | ${'npm'} ${'nuget'} | ${'NuGet'} ${'pypi'} | ${'PyPI'} + ${'rubygems'} | ${'RubyGems'} ${'composer'} | ${'Composer'} ${'foo'} | ${null} `(`package type`, ({ packageType, expectedResult }) => { |