Skip to content

Commit d2fa466

Browse files
committed
fix: load all versions when semver range filter is entered (#1828)
The semver filter only searched against the ~10 versions from the initial SSR payload, so filtering for versions outside that set returned no results. Now, entering a valid semver range triggers loading all versions and auto-expands the "Other versions" section. A guard prevents redundant fetches across subsequent filter changes.
1 parent 036dfc2 commit d2fa466

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

app/components/Package/Versions.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,22 @@ const effectiveCurrentVersion = computed(
115115
116116
// Semver range filter
117117
const semverFilter = ref('')
118+
119+
// Load all versions when a valid semver filter is entered
120+
watch(semverFilter, async newFilter => {
121+
const trimmed = newFilter.trim()
122+
if (trimmed === '' || hasLoadedAll.value) return
123+
if (!validRange(trimmed)) return
124+
125+
try {
126+
const allVersions = await loadAllVersions()
127+
processLoadedVersions(allVersions)
128+
// Auto-expand "Other versions" so filtered results are visible
129+
otherVersionsExpanded.value = true
130+
} catch {
131+
// Silently fail — user can still use the filter with already-known versions
132+
}
133+
})
118134
// Collect all known versions: initial props + dynamically loaded ones
119135
const allKnownVersions = computed(() => {
120136
const versions = new Set(Object.keys(props.versions))

test/nuxt/components/PackageVersions.spec.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,101 @@ describe('PackageVersions', () => {
11271127
})
11281128
})
11291129

1130+
it('loads all versions when a valid semver filter is entered', async () => {
1131+
mockFetchAllPackageVersions.mockResolvedValue([
1132+
{ version: '3.0.0', time: '2024-04-01T00:00:00.000Z', hasProvenance: false },
1133+
{ version: '2.1.0', time: '2024-03-01T00:00:00.000Z', hasProvenance: false },
1134+
{ version: '2.0.0', time: '2024-02-01T00:00:00.000Z', hasProvenance: false },
1135+
{ version: '1.5.0', time: '2024-01-15T00:00:00.000Z', hasProvenance: false },
1136+
{ version: '1.0.0', time: '2024-01-01T00:00:00.000Z', hasProvenance: false },
1137+
])
1138+
1139+
// Only provide a subset of versions in props (simulating initial SSR payload)
1140+
const component = await mountSuspended(PackageVersions, {
1141+
props: {
1142+
packageName: 'test-package',
1143+
versions: {
1144+
'3.0.0': createVersion('3.0.0'),
1145+
},
1146+
distTags: { latest: '3.0.0' },
1147+
time: { '3.0.0': '2024-04-01T00:00:00.000Z' },
1148+
},
1149+
})
1150+
1151+
// Filter for a version NOT in the initial props
1152+
const input = component.find('input[type="text"]')
1153+
await input.setValue('1.5.0')
1154+
1155+
// Should trigger loading all versions
1156+
await vi.waitFor(() => {
1157+
expect(mockFetchAllPackageVersions).toHaveBeenCalledWith('test-package')
1158+
})
1159+
1160+
// After loading, 1.5.0 should appear in the results
1161+
await vi.waitFor(() => {
1162+
const versionLinks = component
1163+
.findAll('a')
1164+
.filter(
1165+
a => !a.attributes('href')?.startsWith('#') && a.attributes('target') !== '_blank',
1166+
)
1167+
const versions = versionLinks.map(l => l.text())
1168+
expect(versions).toContain('1.5.0')
1169+
})
1170+
})
1171+
1172+
it('does not load all versions for invalid semver filter', async () => {
1173+
const component = await mountSuspended(PackageVersions, {
1174+
props: {
1175+
packageName: 'test-package',
1176+
versions: {
1177+
'3.0.0': createVersion('3.0.0'),
1178+
},
1179+
distTags: { latest: '3.0.0' },
1180+
time: { '3.0.0': '2024-04-01T00:00:00.000Z' },
1181+
},
1182+
})
1183+
1184+
const input = component.find('input[type="text"]')
1185+
await input.setValue('not-a-range!!!')
1186+
1187+
// Should NOT trigger loading
1188+
expect(mockFetchAllPackageVersions).not.toHaveBeenCalled()
1189+
})
1190+
1191+
it('only fetches versions once across multiple filter changes', async () => {
1192+
mockFetchAllPackageVersions.mockResolvedValue([
1193+
{ version: '3.0.0', time: '2024-04-01T00:00:00.000Z', hasProvenance: false },
1194+
{ version: '2.0.0', time: '2024-02-01T00:00:00.000Z', hasProvenance: false },
1195+
{ version: '1.0.0', time: '2024-01-01T00:00:00.000Z', hasProvenance: false },
1196+
])
1197+
1198+
const component = await mountSuspended(PackageVersions, {
1199+
props: {
1200+
packageName: 'test-package',
1201+
versions: {
1202+
'3.0.0': createVersion('3.0.0'),
1203+
},
1204+
distTags: { latest: '3.0.0' },
1205+
time: { '3.0.0': '2024-04-01T00:00:00.000Z' },
1206+
},
1207+
})
1208+
1209+
const input = component.find('input[type="text"]')
1210+
1211+
// First valid filter triggers fetch
1212+
await input.setValue('^1.0.0')
1213+
await vi.waitFor(() => {
1214+
expect(mockFetchAllPackageVersions).toHaveBeenCalledTimes(1)
1215+
})
1216+
1217+
// Subsequent filter changes should NOT fetch again
1218+
await input.setValue('^2.0.0')
1219+
await input.setValue('~3.0.0')
1220+
await input.setValue('>=1.0.0')
1221+
1222+
expect(mockFetchAllPackageVersions).toHaveBeenCalledTimes(1)
1223+
})
1224+
11301225
it('filters other major version groups', async () => {
11311226
mockFetchAllPackageVersions.mockResolvedValue([
11321227
{ version: '3.0.0', time: '2024-04-01T00:00:00.000Z', hasProvenance: false },

0 commit comments

Comments
 (0)