diff --git a/app/components/Package/WeeklyDownloadStats.vue b/app/components/Package/WeeklyDownloadStats.vue index 720b3213b..cca88f60c 100644 --- a/app/components/Package/WeeklyDownloadStats.vue +++ b/app/components/Package/WeeklyDownloadStats.vue @@ -12,15 +12,6 @@ const chartModal = useModal('chart-modal') const hasChartModalTransitioned = shallowRef(false) const isChartModalOpen = shallowRef(false) -async function openChartModal() { - isChartModalOpen.value = true - hasChartModalTransitioned.value = false - // ensure the component renders before opening the dialog - await nextTick() - await nextTick() - chartModal.open() -} - function handleModalClose() { isChartModalOpen.value = false hasChartModalTransitioned.value = false @@ -96,10 +87,24 @@ const pulseColor = computed(() => { }) const weeklyDownloads = shallowRef([]) +const isLoadingWeeklyDownloads = shallowRef(true) +const hasWeeklyDownloads = computed(() => weeklyDownloads.value.length > 0) + +async function openChartModal() { + if (!hasWeeklyDownloads.value) return + + isChartModalOpen.value = true + hasChartModalTransitioned.value = false + // ensure the component renders before opening the dialog + await nextTick() + await nextTick() + chartModal.open() +} async function loadWeeklyDownloads() { if (!import.meta.client) return + isLoadingWeeklyDownloads.value = true try { const result = await fetchPackageDownloadEvolution( () => props.packageName, @@ -109,6 +114,8 @@ async function loadWeeklyDownloads() { weeklyDownloads.value = (result as WeeklyDownloadPoint[]) ?? [] } catch { weeklyDownloads.value = [] + } finally { + isLoadingWeeklyDownloads.value = false } } @@ -212,6 +219,7 @@ const config = computed(() => {
- - - - - +

+ {{ $t('package.downloads.no_data') }} +

- + diff --git a/test/nuxt/components/PackageWeeklyDownloadStats.spec.ts b/test/nuxt/components/PackageWeeklyDownloadStats.spec.ts new file mode 100644 index 000000000..2cddc49d0 --- /dev/null +++ b/test/nuxt/components/PackageWeeklyDownloadStats.spec.ts @@ -0,0 +1,65 @@ +import { mockNuxtImport, mountSuspended } from '@nuxt/test-utils/runtime' +import { defineComponent, h } from 'vue' +import { describe, expect, it, vi } from 'vitest' + +const { mockFetchPackageDownloadEvolution } = vi.hoisted(() => ({ + mockFetchPackageDownloadEvolution: vi.fn(), +})) + +mockNuxtImport('useCharts', () => { + return () => ({ + fetchPackageDownloadEvolution: (...args: unknown[]) => + mockFetchPackageDownloadEvolution(...args), + }) +}) + +vi.mock('vue-data-ui/vue-ui-sparkline', () => ({ + VueUiSparkline: defineComponent({ + name: 'VueUiSparkline', + inheritAttrs: false, + setup(_, { attrs, slots }) { + return () => h('div', { class: attrs.class }, slots.default?.() ?? []) + }, + }), +})) + +import PackageWeeklyDownloadStats from '~/components/Package/WeeklyDownloadStats.vue' + +describe('PackageWeeklyDownloadStats', () => { + const baseProps = { + packageName: 'test-package', + createdIso: '2026-02-05T00:00:00.000Z', + } + + it('hides the section when weekly downloads are empty', async () => { + mockFetchPackageDownloadEvolution.mockReset() + mockFetchPackageDownloadEvolution.mockResolvedValue([]) + + const component = await mountSuspended(PackageWeeklyDownloadStats, { + props: baseProps, + }) + + expect(component.text()).toContain('Weekly Downloads') + expect(component.text()).toContain('No download data available') + }) + + it('shows the section when weekly downloads exist', async () => { + mockFetchPackageDownloadEvolution.mockReset() + mockFetchPackageDownloadEvolution.mockResolvedValue([ + { + weekStart: '2026-01-01', + weekEnd: '2026-01-07', + timestampStart: 1767225600000, + timestampEnd: 1767744000000, + downloads: 42, + }, + ]) + + const component = await mountSuspended(PackageWeeklyDownloadStats, { + props: baseProps, + }) + + expect(component.text()).toContain('Weekly Downloads') + expect(component.text()).not.toContain('No download data available') + }) +})