fix(cloud-preview): 🐛 Load all paginated task pages#59
Conversation
AI Code Review SummaryPR: #59 (fix(cloud-preview): 🐛 Load all paginated task pages) Overall AssessmentDetected 4 actionable findings, prioritize CRITICAL/HIGH before merge. Major Findings by Severity
Actionable Suggestions
Potential Risks
Test Suggestions
File-Level Coverage Notes
Inline Downgraded Items (processed but not inline)
Coverage Status
Uncovered list:
No-patch covered list:
Runtime/Budget
|
| expect(window.api.cloud.getPageImage).toHaveBeenCalledWith({ taskId: 'task-1', pageNumber: 1 }) | ||
| }) | ||
|
|
||
| it('fetches all API page chunks when task pages are paginated by backend', async () => { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| }, [id, cloudContext]); | ||
|
|
||
| // Fallback: ensure current page data is available even if backend enforces small page-size caps. | ||
| const ensureCurrentPageLoaded = useCallback(async () => { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| const allPages = [...(result.data || [])]; | ||
| const totalApiPages = result.pagination?.total_pages || 1; | ||
|
|
||
| for (let apiPage = 2; apiPage <= totalApiPages; apiPage++) { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| }, [id, cloudContext]); | ||
|
|
||
| // Fallback: ensure current page data is available even if backend enforces small page-size caps. | ||
| const ensureCurrentPageLoaded = useCallback(async () => { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
src/renderer/pages/CloudPreview.tsx
Outdated
| if (pages.some(page => page.page === currentPage)) return; | ||
|
|
||
| try { | ||
| const result = await cloudContext.getTaskPages(id, currentPage, 1); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
src/renderer/pages/CloudPreview.tsx
Outdated
| } | ||
| } | ||
|
|
||
| const dedupedSortedPages = Array.from( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| setPages(result.data); | ||
| const requestedPageSize = 100; | ||
| const result = await cloudContext.getTaskPages(id, 1, requestedPageSize); | ||
| if (result.success) { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
src/renderer/pages/CloudPreview.tsx
Outdated
|
|
||
| inFlightFallbackPagesRef.current.add(currentPage); | ||
| try { | ||
| const result = await cloudContext.getTaskPages(id, currentPage, 1); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| if (result.success && pageItems?.length) { | ||
| setPages(prev => dedupeAndSortPages([...prev, ...pageItems])); | ||
| } | ||
| } catch { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| cloudEventCb?.({ type: 'completed', data: { task_id: 'task-1', status: 6, pages_completed: 2, pages_failed: 0 } }) | ||
| cloudEventCb?.({ type: 'completed', data: { task_id: 'task-1', status: 6, pages_completed: 2, pages_failed: 0 } }) | ||
|
|
||
| await new Promise(resolve => setTimeout(resolve, 30)) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
src/renderer/pages/CloudPreview.tsx
Outdated
| if (attemptedFallbackPagesRef.current.has(currentPage) || inFlightFallbackPagesRef.current.has(currentPage)) return; | ||
|
|
||
| const effectivePageSize = Math.max(1, taskPagesPageSizeRef.current || 1); | ||
| const fallbackApiPage = Math.max(1, Math.floor((currentPage - 1) / effectivePageSize) + 1); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| } | ||
| const pageItems = (result.data || []).filter(page => page.page === currentPage); | ||
| if (result.success && pageItems?.length) { | ||
| setPages(prev => dedupeAndSortPages([...prev, ...pageItems])); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| }) | ||
| }) | ||
|
|
||
| it('keeps successfully fetched chunks when a later API page chunk fails', async () => { |
There was a problem hiding this comment.
[MEDIUM] Missing assertion for chunk-fetch error observability path
A test covers chunk failure recovery but does not assert that the error-reporting path is exercised, so logging/telemetry regressions could slip through unnoticed.
Suggestion: Spy on console.error (or your logging utility) and assert it is called with the chunk-specific message when a later page chunk throws. This ensures observability behavior is protected, not just UI continuity.
Risk: Silent failures in production become harder to diagnose if logging behavior regresses while tests still pass.
Confidence: 0.88
|
|
||
| fireEvent.click(screen.getByRole('button', { name: 'Switch Task' })) | ||
|
|
||
| await waitFor(() => { |
There was a problem hiding this comment.
[MEDIUM] Route-change reset test asserts fallback call but not primary reload call, risking false positives
The route-change test validates a fallback fetch for task-2 but does not assert the expected initial fetch (task-2, page 1, size 100), so it may pass even if core page reload behavior breaks.
Suggestion: Add an explicit expectation that getTaskPages('task-2', 1, 100) is called after switching task IDs, then separately assert fallback behavior as needed.
Risk: A regression where new task pages are not fully reloaded on route change could go undetected while the test still passes due to fallback-only calls.
Confidence: 0.85
| }, [id, cloudContext]); | ||
|
|
||
| // Fallback: ensure current page data is available even if backend enforces small page-size caps. | ||
| const ensureCurrentPageLoaded = useCallback(async () => { |
There was a problem hiding this comment.
[MEDIUM] Fallback may query previous task pages after route id change due to stale currentPage
When the task id changes, fallback logic can request a computed API page for the previous task's selected page index because currentPage is not reset with id transition.
Suggestion: Reset currentPage deterministically when id changes (e.g., to 1) before fallback runs, or guard ensureCurrentPageLoaded with current task metadata readiness and page bounds (currentPage <= task.page_count). Also update tests to assert first-page fetch after switch, not stale-page fallback.
Risk: Incorrect page requests after navigation can show wrong/empty content, add unnecessary API traffic, and create confusing UX on task switches.
Confidence: 0.93
| expect(screen.getByText(/6 pages/)).toBeInTheDocument() | ||
| }) | ||
|
|
||
| const pageFiveAnchor = document.querySelector('li[title="5"] a') as HTMLElement | null |
There was a problem hiding this comment.
[LOW] Pagination interaction tests rely on CSS selector querying (li[title] a), increasing flakiness
Several new tests use implementation-detail CSS selectors for pagination controls, which are fragile against AntD markup/version changes and can cause flaky failures.
Suggestion: Prefer RTL semantic queries (e.g., getByRole('button', { name: '5' }) if available) or add stable data-testid hooks around pagination items in component markup for deterministic targeting.
Risk: Non-functional DOM structure changes may break tests unexpectedly, reducing signal quality and increasing maintenance churn.
Confidence: 0.93
Summary
Test Plan
CloudPreviewpagination coverage passes.🤖 Generated with Codex Cli