text: 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),
        );
      });
    });
  });
  