-
-
Notifications
You must be signed in to change notification settings - Fork 375
fix: cap pagination to maximum fetchable results #2205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
|
|
||
| /** | ||
| * Tests for the pagination total capping logic used in search.vue. | ||
| * | ||
| * The search page caps the displayed pagination total to EAGER_LOAD_SIZE | ||
| * so that page links only reflect pages that can actually be fetched. | ||
| * Without the cap, a search returning total=92,000 items would show 3,680 | ||
| * pages at 25 items/page, but navigation beyond page 20 silently fails. | ||
| */ | ||
|
|
||
| const EAGER_LOAD_SIZE = { algolia: 500, npm: 500 } as const | ||
|
|
||
| function paginationTotal(effectiveTotal: number, provider: keyof typeof EAGER_LOAD_SIZE): number { | ||
| const cap = EAGER_LOAD_SIZE[provider] | ||
| return Math.min(effectiveTotal, cap) | ||
| } | ||
|
Comment on lines
+12
to
+17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test re-implements production logic instead of exercising a shared source of truth. Lines 12-17 duplicate both Refactor direction (shared util + direct import in tests)-const EAGER_LOAD_SIZE = { algolia: 500, npm: 500 } as const
-
-function paginationTotal(effectiveTotal: number, provider: keyof typeof EAGER_LOAD_SIZE): number {
- const cap = EAGER_LOAD_SIZE[provider]
- return Math.min(effectiveTotal, cap)
-}
+import { EAGER_LOAD_SIZE, getPaginationTotal } from '~/utils/pagination'- expect(paginationTotal(100, 'npm')).toBe(100)
+ expect(getPaginationTotal(100, 'npm')).toBe(100)(Apply same replacement across the file, and use the same util in |
||
|
|
||
| describe('paginationTotal capping logic', () => { | ||
| it('returns the total as-is when it is below the cap', () => { | ||
| expect(paginationTotal(100, 'npm')).toBe(100) | ||
| expect(paginationTotal(100, 'algolia')).toBe(100) | ||
| }) | ||
|
|
||
| it('returns the cap when the total exceeds it', () => { | ||
| expect(paginationTotal(92_000, 'npm')).toBe(500) | ||
| expect(paginationTotal(92_000, 'algolia')).toBe(500) | ||
| }) | ||
|
|
||
| it('returns exactly the cap when the total equals the cap', () => { | ||
| expect(paginationTotal(500, 'npm')).toBe(500) | ||
| expect(paginationTotal(500, 'algolia')).toBe(500) | ||
| }) | ||
|
|
||
| it('returns 0 when total is 0', () => { | ||
| expect(paginationTotal(0, 'npm')).toBe(0) | ||
| }) | ||
|
|
||
| it('caps algolia and npm identically (both have 500 limit)', () => { | ||
| const total = 10_000 | ||
| expect(paginationTotal(total, 'algolia')).toBe(paginationTotal(total, 'npm')) | ||
| }) | ||
|
|
||
| it('page count derived from capped total stays within fetchable range', () => { | ||
| const pageSize = 25 | ||
| const rawTotal = 92_000 | ||
| const cappedTotal = paginationTotal(rawTotal, 'npm') | ||
| const maxPages = Math.ceil(cappedTotal / pageSize) | ||
| // Should be 20 pages (500 / 25), not 3680 (92000 / 25) | ||
| expect(maxPages).toBe(20) | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: npmx-dev/npmx.dev
Length of output: 7069
Clamp
currentPageagainst capped max pages to cover deep-link edge cases.URL-derived
currentPagecan exceed the capped pagination range (e.g.,?page=999from stale links). WhilstPaginationControlsclamps page changes via its internalgoToPage()guard, the initialcurrentPagevalue set on mount from the URL bypasses this check, resulting in an invalid state where the displayed pagination text and item range become incoherent (e.g., "Showing items 24976 to 500" when total is capped at 500).Add a watch to clamp
currentPagewhenpaginationTotalorpreferredPageSizechanges:Suggested clamp in
app/pages/search.vueconst paginationTotal = computed(() => { const cap = EAGER_LOAD_SIZE[searchProvider.value] return Math.min(effectiveTotal.value, cap) }) + +const maxFetchablePages = computed(() => + Math.max(1, Math.ceil(paginationTotal.value / preferredPageSize.value)), +) + +watch([maxFetchablePages, currentPage], ([maxPages, page]) => { + if (page > maxPages) { + currentPage.value = maxPages + updateUrlPage(maxPages) + } +})📝 Committable suggestion