import { GlIcon, GlSprintf } from '@gitlab/ui'; import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import PackageTags from '~/packages/shared/components/package_tags.vue'; import PackageTitle from '~/packages_and_registries/package_registry/components/details/package_title.vue'; import { PACKAGE_TYPE_CONAN, PACKAGE_TYPE_MAVEN, PACKAGE_TYPE_NPM, PACKAGE_TYPE_NUGET, } from '~/packages_and_registries/package_registry/constants'; import TitleArea from '~/vue_shared/components/registry/title_area.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import { packageData, packageFiles, packageTags, packagePipelines } from '../../mock_data'; const packageWithTags = { ...packageData(), tags: { nodes: packageTags() }, packageFiles: { nodes: packageFiles() }, }; describe('PackageTitle', () => { let wrapper; function createComponent(packageEntity = packageWithTags) { wrapper = shallowMountExtended(PackageTitle, { propsData: { packageEntity }, stubs: { TitleArea, GlSprintf, }, directives: { GlResizeObserver: createMockDirective(), }, }); return wrapper.vm.$nextTick(); } const findTitleArea = () => wrapper.findComponent(TitleArea); const findPackageType = () => wrapper.findByTestId('package-type'); const findPackageSize = () => wrapper.findByTestId('package-size'); const findPipelineProject = () => wrapper.findByTestId('pipeline-project'); const findPackageRef = () => wrapper.findByTestId('package-ref'); const findPackageTags = () => wrapper.findComponent(PackageTags); const findPackageBadges = () => wrapper.findAllByTestId('tag-badge'); const findSubHeaderIcon = () => wrapper.findComponent(GlIcon); const findSubHeaderText = () => wrapper.findByTestId('sub-header'); const findSubHeaderTimeAgo = () => wrapper.findComponent(TimeAgoTooltip); afterEach(() => { wrapper.destroy(); }); describe('renders', () => { it('without tags', async () => { await createComponent({ ...packageData(), packageFiles: { nodes: packageFiles() } }); expect(wrapper.element).toMatchSnapshot(); }); it('with tags', async () => { await createComponent(); expect(wrapper.element).toMatchSnapshot(); }); it('with tags on mobile', async () => { jest.spyOn(GlBreakpointInstance, 'isDesktop').mockReturnValue(false); await createComponent(); await wrapper.vm.$nextTick(); expect(findPackageBadges()).toHaveLength(packageTags().length); }); it('when the page is resized', async () => { await createComponent(); expect(findPackageBadges()).toHaveLength(0); jest.spyOn(GlBreakpointInstance, 'isDesktop').mockReturnValue(false); const { value } = getBinding(wrapper.element, 'gl-resize-observer'); value(); await wrapper.vm.$nextTick(); expect(findPackageBadges()).toHaveLength(packageTags().length); }); }); describe('package title', () => { it('is correctly bound', async () => { await createComponent(); expect(findTitleArea().props('title')).toBe(packageData().name); }); }); describe('package icon', () => { const iconUrl = 'a-fake-src'; it('shows an icon when present and package type is NUGET', async () => { await createComponent({ ...packageData(), packageType: PACKAGE_TYPE_NUGET, metadata: { iconUrl }, }); expect(findTitleArea().props('avatar')).toBe(iconUrl); }); it('hides the icon when not present', async () => { await createComponent(); expect(findTitleArea().props('avatar')).toBe(null); }); }); describe('sub-header', () => { it('has the eye icon', async () => { await createComponent(); expect(findSubHeaderIcon().props('name')).toBe('eye'); }); it('has a text showing version', async () => { await createComponent(); expect(findSubHeaderText().text()).toMatchInterpolatedText('v 1.0.0 published'); }); it('has a time ago tooltip component', async () => { await createComponent(); expect(findSubHeaderTimeAgo().props('time')).toBe(packageWithTags.createdAt); }); }); describe.each` packageType | text ${PACKAGE_TYPE_CONAN} | ${'Conan'} ${PACKAGE_TYPE_MAVEN} | ${'Maven'} ${PACKAGE_TYPE_NPM} | ${'npm'} ${PACKAGE_TYPE_NUGET} | ${'NuGet'} `(`package type`, ({ packageType, text }) => { beforeEach(() => createComponent({ ...packageData, packageType })); it(`${packageType} should render ${text}`, () => { expect(findPackageType().props()).toEqual(expect.objectContaining({ text, icon: 'package' })); }); }); describe('calculates the package size', () => { it('correctly calculates when there is only 1 file', async () => { await createComponent({ ...packageData(), packageFiles: { nodes: [packageFiles()[0]] } }); expect(findPackageSize().props()).toMatchObject({ text: '400.00 KiB', icon: 'disk' }); }); it('correctly calculates when there are multiple files', async () => { await createComponent(); expect(findPackageSize().props('text')).toBe('800.00 KiB'); }); }); describe('package tags', () => { it('displays the package-tags component when the package has tags', async () => { await createComponent(); expect(findPackageTags().exists()).toBe(true); }); it('does not display the package-tags component when there are no tags', async () => { await createComponent({ ...packageData(), tags: { nodes: [] } }); expect(findPackageTags().exists()).toBe(false); }); }); describe('package ref', () => { it('does not display the ref if missing', async () => { await createComponent(); expect(findPackageRef().exists()).toBe(false); }); it('correctly shows the package ref if there is one', async () => { await createComponent({ ...packageData(), pipelines: { nodes: packagePipelines({ ref: 'test' }) }, }); expect(findPackageRef().props()).toMatchObject({ text: 'test', icon: 'branch', }); }); }); describe('pipeline project', () => { it('does not display the project if missing', async () => { await createComponent(); expect(findPipelineProject().exists()).toBe(false); }); it('correctly shows the pipeline project if there is one', async () => { await createComponent({ ...packageData(), pipelines: { nodes: packagePipelines() }, }); expect(findPipelineProject().props()).toMatchObject({ text: packagePipelines()[0].project.name, icon: 'review-list', link: packagePipelines()[0].project.webUrl, }); }); }); });