[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 190 lines 6.2 kB view raw
1import { afterEach, describe, expect, it, vi } from 'vitest' 2import { mountSuspended } from '@nuxt/test-utils/runtime' 3import type { PackageComparisonData } from '~/composables/usePackageComparison' 4 5/** 6 * Helper to test usePackageComparison by wrapping it in a component. 7 * This is required because the composable uses useI18n which must be 8 * called inside a Vue component's setup function. 9 */ 10async function usePackageComparisonInComponent(packageNames: string[]) { 11 // Create refs to capture the composable's return values 12 const capturedPackagesData = ref<(PackageComparisonData | null)[]>([]) as Ref< 13 (PackageComparisonData | null)[] 14 > 15 const capturedStatus = ref<'idle' | 'pending' | 'success' | 'error'>('idle') as Ref< 16 'idle' | 'pending' | 'success' | 'error' 17 > 18 let capturedGetFacetValues: (facet: ComparisonFacet) => (FacetValue | null)[] 19 20 const WrapperComponent = defineComponent({ 21 setup() { 22 const { packagesData, status, getFacetValues } = usePackageComparison(packageNames) 23 24 // Sync values to captured refs 25 watchEffect(() => { 26 capturedPackagesData.value = [...packagesData.value] 27 capturedStatus.value = status.value 28 }) 29 capturedGetFacetValues = getFacetValues 30 31 return () => h('div') 32 }, 33 }) 34 35 await mountSuspended(WrapperComponent) 36 37 return { 38 packagesData: capturedPackagesData, 39 status: capturedStatus, 40 getFacetValues: (facet: ComparisonFacet) => capturedGetFacetValues(facet), 41 } 42} 43 44describe('usePackageComparison', () => { 45 afterEach(() => { 46 vi.unstubAllGlobals() 47 }) 48 49 describe('lastUpdated facet', () => { 50 it('uses version-specific publish date, not time.modified', async () => { 51 vi.stubGlobal( 52 '$fetch', 53 vi.fn().mockImplementation((url: string) => { 54 if (url.startsWith('https://registry.npmjs.org/')) { 55 return Promise.resolve({ 56 'name': 'test-package', 57 'dist-tags': { latest: '2.0.0' }, 58 'time': { 59 // This is the WRONG value - updated by metadata changes 60 'modified': '2024-12-01T00:00:00.000Z', 61 // This is the CORRECT value - actual publish date 62 '2.0.0': '2024-06-15T00:00:00.000Z', 63 }, 64 'license': 'MIT', 65 'versions': { 66 '2.0.0': { dist: { unpackedSize: 15000 } }, 67 }, 68 }) 69 } 70 return Promise.resolve(null) 71 }), 72 ) 73 74 const { packagesData, status, getFacetValues } = await usePackageComparisonInComponent([ 75 'test-package', 76 ]) 77 78 await vi.waitFor(() => { 79 expect(status.value).toBe('success') 80 }) 81 82 expect(packagesData.value[0]).not.toBeNull() 83 84 const values = getFacetValues('lastUpdated') 85 expect(values).toHaveLength(1) 86 expect(values[0]).not.toBeNull() 87 88 // Should use version-specific timestamp, NOT time.modified 89 expect(values[0]!.display).toBe('2024-06-15T00:00:00.000Z') 90 expect(values[0]!.raw).toBe(new Date('2024-06-15T00:00:00.000Z').getTime()) 91 }) 92 93 it('stores version-specific time in metadata', async () => { 94 vi.stubGlobal( 95 '$fetch', 96 vi.fn().mockImplementation((url: string) => { 97 if (url.startsWith('https://registry.npmjs.org/')) { 98 return Promise.resolve({ 99 'name': 'test-package', 100 'dist-tags': { latest: '1.0.0' }, 101 'time': { 102 'modified': '2025-01-01T00:00:00.000Z', 103 '1.0.0': '2023-03-20T00:00:00.000Z', 104 }, 105 'license': 'MIT', 106 'versions': { 107 '1.0.0': { dist: { unpackedSize: 10000 } }, 108 }, 109 }) 110 } 111 return Promise.resolve(null) 112 }), 113 ) 114 115 const { packagesData, status } = await usePackageComparisonInComponent(['test-package']) 116 117 await vi.waitFor(() => { 118 expect(status.value).toBe('success') 119 }) 120 121 const metadata = packagesData.value[0]?.metadata 122 expect(metadata?.lastUpdated).toBe('2023-03-20T00:00:00.000Z') 123 expect(metadata?.lastUpdated).not.toBe('2025-01-01T00:00:00.000Z') 124 }) 125 }) 126 127 describe('staleness detection', () => { 128 it('marks packages not published in 2+ years as stale', async () => { 129 vi.stubGlobal( 130 '$fetch', 131 vi.fn().mockImplementation((url: string) => { 132 if (url.startsWith('https://registry.npmjs.org/')) { 133 return Promise.resolve({ 134 'name': 'old-package', 135 'dist-tags': { latest: '1.0.0' }, 136 'time': { 137 '1.0.0': '2020-01-01T00:00:00.000Z', // More than 2 years ago 138 }, 139 'license': 'MIT', 140 'versions': { 141 '1.0.0': { dist: { unpackedSize: 5000 } }, 142 }, 143 }) 144 } 145 return Promise.resolve(null) 146 }), 147 ) 148 149 const { status, getFacetValues } = await usePackageComparisonInComponent(['old-package']) 150 151 await vi.waitFor(() => { 152 expect(status.value).toBe('success') 153 }) 154 155 const values = getFacetValues('lastUpdated') 156 expect(values[0]?.status).toBe('warning') 157 }) 158 159 it('marks recently published packages as neutral', async () => { 160 vi.stubGlobal( 161 '$fetch', 162 vi.fn().mockImplementation((url: string) => { 163 if (url.startsWith('https://registry.npmjs.org/')) { 164 return Promise.resolve({ 165 'name': 'fresh-package', 166 'dist-tags': { latest: '1.0.0' }, 167 'time': { 168 '1.0.0': new Date().toISOString(), // Today 169 }, 170 'license': 'MIT', 171 'versions': { 172 '1.0.0': { dist: { unpackedSize: 5000 } }, 173 }, 174 }) 175 } 176 return Promise.resolve(null) 177 }), 178 ) 179 180 const { status, getFacetValues } = await usePackageComparisonInComponent(['fresh-package']) 181 182 await vi.waitFor(() => { 183 expect(status.value).toBe('success') 184 }) 185 186 const values = getFacetValues('lastUpdated') 187 expect(values[0]?.status).toBe('neutral') 188 }) 189 }) 190})