debian-mirror-gitlab/spec/frontend/packages/details/components/app_spec.js
2021-10-27 15:23:28 +05:30

385 lines
12 KiB
JavaScript

import { GlEmptyState } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import stubChildren from 'helpers/stub_children';
import AdditionalMetadata from '~/packages/details/components/additional_metadata.vue';
import PackagesApp from '~/packages/details/components/app.vue';
import DependencyRow from '~/packages/details/components/dependency_row.vue';
import InstallationCommands from '~/packages/details/components/installation_commands.vue';
import PackageFiles from '~/packages/details/components/package_files.vue';
import PackageHistory from '~/packages/details/components/package_history.vue';
import PackageTitle from '~/packages/details/components/package_title.vue';
import * as getters from '~/packages/details/store/getters';
import PackageListRow 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 Tracking from '~/tracking';
import {
composerPackage,
conanPackage,
mavenPackage,
mavenFiles,
npmPackage,
nugetPackage,
} from '../../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
useMockLocationHelper();
describe('PackagesApp', () => {
let wrapper;
let store;
const fetchPackageVersions = jest.fn();
const deletePackage = jest.fn();
const deletePackageFile = jest.fn();
const defaultProjectName = 'bar';
function createComponent({
packageEntity = mavenPackage,
packageFiles = mavenFiles,
isLoading = false,
projectName = defaultProjectName,
} = {}) {
store = new Vuex.Store({
state: {
isLoading,
packageEntity,
packageFiles,
canDelete: true,
emptySvgPath: 'empty-illustration',
npmPath: 'foo',
npmHelpPath: 'foo',
projectName,
projectListUrl: 'project_url',
groupListUrl: 'group_url',
},
actions: {
deletePackage,
fetchPackageVersions,
deletePackageFile,
},
getters,
});
wrapper = mount(PackagesApp, {
localVue,
store,
stubs: {
...stubChildren(PackagesApp),
PackageTitle: false,
TitleArea: false,
GlButton: false,
GlModal: false,
GlTab: false,
GlTabs: false,
GlTable: false,
},
});
}
const packageTitle = () => wrapper.find(PackageTitle);
const emptyState = () => wrapper.find(GlEmptyState);
const deleteButton = () => wrapper.find('.js-delete-button');
const findDeleteModal = () => wrapper.find({ ref: 'deleteModal' });
const findDeleteFileModal = () => wrapper.find({ ref: 'deleteFileModal' });
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);
const findPackageFiles = () => wrapper.find(PackageFiles);
afterEach(() => {
wrapper.destroy();
});
it('renders the app and displays the package title', async () => {
createComponent();
await nextTick();
expect(packageTitle().exists()).toBe(true);
});
it('renders an empty state component when no an invalid package is passed as a prop', () => {
createComponent({
packageEntity: {},
});
expect(emptyState().exists()).toBe(true);
});
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(findPackageFiles().exists()).toBe(false);
});
describe('deleting packages', () => {
beforeEach(() => {
createComponent();
deleteButton().trigger('click');
});
it('shows the delete confirmation modal when delete is clicked', () => {
expect(findDeleteModal().exists()).toBe(true);
});
});
describe('deleting package files', () => {
it('shows the delete confirmation modal when delete is clicked', () => {
createComponent();
findPackageFiles().vm.$emit('delete-file', mavenFiles[0]);
expect(findDeleteFileModal().exists()).toBe(true);
});
});
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 and delete', () => {
describe('delete package', () => {
const originalReferrer = document.referrer;
const setReferrer = (value = defaultProjectName) => {
Object.defineProperty(document, 'referrer', {
value,
configurable: true,
});
};
afterEach(() => {
Object.defineProperty(document, 'referrer', {
value: originalReferrer,
configurable: true,
});
});
it('calls the proper vuex action', () => {
createComponent({ packageEntity: npmPackage });
findDeleteModal().vm.$emit('primary');
expect(deletePackage).toHaveBeenCalled();
});
it('when referrer contains project name calls window.replace with project url', async () => {
setReferrer();
deletePackage.mockResolvedValue();
createComponent({ packageEntity: npmPackage });
findDeleteModal().vm.$emit('primary');
await deletePackage();
expect(window.location.replace).toHaveBeenCalledWith(
'project_url?showSuccessDeleteAlert=true',
);
});
it('when referrer does not contain project name calls window.replace with group url', async () => {
setReferrer('baz');
deletePackage.mockResolvedValue();
createComponent({ packageEntity: npmPackage });
findDeleteModal().vm.$emit('primary');
await deletePackage();
expect(window.location.replace).toHaveBeenCalledWith(
'group_url?showSuccessDeleteAlert=true',
);
});
});
describe('delete file', () => {
it('calls the proper vuex action', () => {
createComponent({ packageEntity: npmPackage });
findPackageFiles().vm.$emit('delete-file', mavenFiles[0]);
findDeleteFileModal().vm.$emit('primary');
expect(deletePackageFile).toHaveBeenCalled();
});
});
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: npmPackage });
findDeleteModal().vm.$emit('primary');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.DELETE_PACKAGE,
expect.any(Object),
);
});
it(`canceling a package deletion tracks ${TrackingActions.CANCEL_DELETE_PACKAGE}`, () => {
createComponent({ packageEntity: npmPackage });
findDeleteModal().vm.$emit('canceled');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.CANCEL_DELETE_PACKAGE,
expect.any(Object),
);
});
it(`request a file deletion tracks ${TrackingActions.REQUEST_DELETE_PACKAGE_FILE}`, () => {
createComponent({ packageEntity: npmPackage });
findPackageFiles().vm.$emit('delete-file', mavenFiles[0]);
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.REQUEST_DELETE_PACKAGE_FILE,
expect.any(Object),
);
});
it(`confirming a file deletion tracks ${TrackingActions.DELETE_PACKAGE_FILE}`, () => {
createComponent({ packageEntity: npmPackage });
findPackageFiles().vm.$emit('delete-file', npmPackage);
findDeleteFileModal().vm.$emit('primary');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.REQUEST_DELETE_PACKAGE_FILE,
expect.any(Object),
);
});
it(`canceling a file deletion tracks ${TrackingActions.CANCEL_DELETE_PACKAGE_FILE}`, () => {
createComponent({ packageEntity: npmPackage });
findPackageFiles().vm.$emit('delete-file', npmPackage);
findDeleteFileModal().vm.$emit('canceled');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.CANCEL_DELETE_PACKAGE_FILE,
expect.any(Object),
);
});
it(`file download link call event with ${TrackingActions.PULL_PACKAGE}`, () => {
createComponent({ packageEntity: conanPackage });
findPackageFiles().vm.$emit('download-file');
expect(eventSpy).toHaveBeenCalledWith(
category,
TrackingActions.PULL_PACKAGE,
expect.any(Object),
);
});
});
});
});