diff options
Diffstat (limited to 'spec/frontend/packages/details/components/app_spec.js')
-rw-r--r-- | spec/frontend/packages/details/components/app_spec.js | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/spec/frontend/packages/details/components/app_spec.js b/spec/frontend/packages/details/components/app_spec.js new file mode 100644 index 00000000000..f535f3f5744 --- /dev/null +++ b/spec/frontend/packages/details/components/app_spec.js @@ -0,0 +1,281 @@ +import Vuex from 'vuex'; +import { mount, createLocalVue } from '@vue/test-utils'; +import { GlEmptyState, GlModal } from '@gitlab/ui'; +import stubChildren from 'helpers/stub_children'; +import Tracking from '~/tracking'; +import * as getters from '~/packages/details/store/getters'; +import PackagesApp from '~/packages/details/components/app.vue'; +import PackageTitle from '~/packages/details/components/package_title.vue'; + +import * as SharedUtils from '~/packages/shared/utils'; +import { TrackingActions } from '~/packages/shared/constants'; +import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue'; +import PackageListRow from '~/packages/shared/components/package_list_row.vue'; + +import DependencyRow from '~/packages/details/components/dependency_row.vue'; +import PackageHistory from '~/packages/details/components/package_history.vue'; +import AdditionalMetadata from '~/packages/details/components/additional_metadata.vue'; +import InstallationCommands from '~/packages/details/components/installation_commands.vue'; + +import { + composerPackage, + conanPackage, + mavenPackage, + mavenFiles, + npmPackage, + npmFiles, + nugetPackage, +} from '../../mock_data'; + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe('PackagesApp', () => { + let wrapper; + let store; + const fetchPackageVersions = jest.fn(); + + function createComponent({ + packageEntity = mavenPackage, + packageFiles = mavenFiles, + isLoading = false, + oneColumnView = false, + } = {}) { + store = new Vuex.Store({ + state: { + isLoading, + packageEntity, + packageFiles, + canDelete: true, + destroyPath: 'destroy-package-path', + emptySvgPath: 'empty-illustration', + npmPath: 'foo', + npmHelpPath: 'foo', + projectName: 'bar', + oneColumnView, + }, + actions: { + fetchPackageVersions, + }, + getters, + }); + + wrapper = mount(PackagesApp, { + localVue, + store, + stubs: { + ...stubChildren(PackagesApp), + GlButton: false, + GlModal: false, + GlTab: false, + GlTabs: false, + GlTable: false, + }, + }); + } + + const packageTitle = () => wrapper.find(PackageTitle); + const emptyState = () => wrapper.find(GlEmptyState); + const allFileRows = () => wrapper.findAll('.js-file-row'); + const firstFileDownloadLink = () => wrapper.find('.js-file-download'); + const deleteButton = () => wrapper.find('.js-delete-button'); + const deleteModal = () => wrapper.find(GlModal); + const modalDeleteButton = () => wrapper.find({ ref: 'modal-delete-button' }); + const versionsTab = () => wrapper.find('.js-versions-tab > a'); + const packagesLoader = () => wrapper.find(PackagesListLoader); + const packagesVersionRows = () => wrapper.findAll(PackageListRow); + const noVersionsMessage = () => wrapper.find('[data-testid="no-versions-message"]'); + const dependenciesTab = () => wrapper.find('.js-dependencies-tab > a'); + const dependenciesCountBadge = () => wrapper.find('[data-testid="dependencies-badge"]'); + const noDependenciesMessage = () => wrapper.find('[data-testid="no-dependencies-message"]'); + const dependencyRows = () => wrapper.findAll(DependencyRow); + const findPackageHistory = () => wrapper.find(PackageHistory); + const findAdditionalMetadata = () => wrapper.find(AdditionalMetadata); + const findInstallationCommands = () => wrapper.find(InstallationCommands); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the app and displays the package title', () => { + createComponent(); + + expect(packageTitle()).toExist(); + }); + + it('renders an empty state component when no an invalid package is passed as a prop', () => { + createComponent({ + packageEntity: {}, + }); + + expect(emptyState()).toExist(); + }); + + it('package history has the right props', () => { + createComponent(); + expect(findPackageHistory().exists()).toBe(true); + expect(findPackageHistory().props('packageEntity')).toEqual(wrapper.vm.packageEntity); + expect(findPackageHistory().props('projectName')).toEqual(wrapper.vm.projectName); + }); + + it('additional metadata has the right props', () => { + createComponent(); + expect(findAdditionalMetadata().exists()).toBe(true); + expect(findAdditionalMetadata().props('packageEntity')).toEqual(wrapper.vm.packageEntity); + }); + + it('installation commands has the right props', () => { + createComponent(); + expect(findInstallationCommands().exists()).toBe(true); + expect(findInstallationCommands().props('packageEntity')).toEqual(wrapper.vm.packageEntity); + }); + + it('hides the files table if package type is COMPOSER', () => { + createComponent({ packageEntity: composerPackage }); + expect(allFileRows().exists()).toBe(false); + }); + + it('renders a single file for an npm package as they only contain one file', () => { + createComponent({ packageEntity: npmPackage, packageFiles: npmFiles }); + + expect(allFileRows()).toExist(); + expect(allFileRows()).toHaveLength(1); + }); + + it('renders multiple files for a package that contains more than one file', () => { + createComponent(); + + expect(allFileRows()).toExist(); + expect(allFileRows()).toHaveLength(2); + }); + + it('allows the user to download a package file by rendering a download link', () => { + createComponent(); + + expect(allFileRows()).toExist(); + expect(firstFileDownloadLink().vm.$attrs.href).toContain('download'); + }); + + describe('deleting packages', () => { + beforeEach(() => { + createComponent(); + deleteButton().trigger('click'); + }); + + it('shows the delete confirmation modal when delete is clicked', () => { + expect(deleteModal()).toExist(); + }); + }); + + describe('versions', () => { + describe('api call', () => { + beforeEach(() => { + createComponent(); + }); + + it('makes api request on first click of tab', () => { + versionsTab().trigger('click'); + + expect(fetchPackageVersions).toHaveBeenCalled(); + }); + }); + + it('displays the loader when state is loading', () => { + createComponent({ isLoading: true }); + + expect(packagesLoader().exists()).toBe(true); + }); + + it('displays the correct version count when the package has versions', () => { + createComponent({ packageEntity: npmPackage }); + + expect(packagesVersionRows()).toHaveLength(npmPackage.versions.length); + }); + + it('displays the no versions message when there are none', () => { + createComponent(); + + expect(noVersionsMessage().exists()).toBe(true); + }); + }); + + describe('dependency links', () => { + it('does not show the dependency links for a non nuget package', () => { + createComponent(); + + expect(dependenciesTab().exists()).toBe(false); + }); + + it('shows the dependencies tab with 0 count when a nuget package with no dependencies', () => { + createComponent({ + packageEntity: { + ...nugetPackage, + dependency_links: [], + }, + }); + + return wrapper.vm.$nextTick(() => { + const dependenciesBadge = dependenciesCountBadge(); + + expect(dependenciesTab().exists()).toBe(true); + expect(dependenciesBadge.exists()).toBe(true); + expect(dependenciesBadge.text()).toBe('0'); + expect(noDependenciesMessage().exists()).toBe(true); + }); + }); + + it('renders the correct number of dependency rows for a nuget package', () => { + createComponent({ packageEntity: nugetPackage }); + + return wrapper.vm.$nextTick(() => { + const dependenciesBadge = dependenciesCountBadge(); + + expect(dependenciesTab().exists()).toBe(true); + expect(dependenciesBadge.exists()).toBe(true); + expect(dependenciesBadge.text()).toBe(nugetPackage.dependency_links.length.toString()); + expect(dependencyRows()).toHaveLength(nugetPackage.dependency_links.length); + }); + }); + }); + + describe('tracking', () => { + let eventSpy; + let utilSpy; + const category = 'foo'; + + beforeEach(() => { + eventSpy = jest.spyOn(Tracking, 'event'); + utilSpy = jest.spyOn(SharedUtils, 'packageTypeToTrackCategory').mockReturnValue(category); + }); + + it('tracking category calls packageTypeToTrackCategory', () => { + createComponent({ packageEntity: conanPackage }); + expect(wrapper.vm.tracking.category).toBe(category); + expect(utilSpy).toHaveBeenCalledWith('conan'); + }); + + it(`delete button on delete modal call event with ${TrackingActions.DELETE_PACKAGE}`, () => { + createComponent({ packageEntity: conanPackage }); + deleteButton().trigger('click'); + return wrapper.vm.$nextTick().then(() => { + modalDeleteButton().trigger('click'); + expect(eventSpy).toHaveBeenCalledWith( + category, + TrackingActions.DELETE_PACKAGE, + expect.any(Object), + ); + }); + }); + + it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => { + createComponent({ packageEntity: conanPackage }); + + firstFileDownloadLink().vm.$emit('click'); + expect(eventSpy).toHaveBeenCalledWith( + category, + TrackingActions.PULL_PACKAGE, + expect.any(Object), + ); + }); + }); +}); |