From 4f89dec127c5c2b835c7753ffc80d9821f96d397 Mon Sep 17 00:00:00 2001 From: Suresh <115452537+sureshsuriya@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:57:34 +0530 Subject: [PATCH] fix(badge): reset generationKey to re-trigger badge loading on repeat generation (#5694) --- app/components/LandingPageClient.tsx | 9 +++- app/page.test.tsx | 64 ++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/app/components/LandingPageClient.tsx b/app/components/LandingPageClient.tsx index 4e8fe58bc..fdbf89262 100644 --- a/app/components/LandingPageClient.tsx +++ b/app/components/LandingPageClient.tsx @@ -310,6 +310,7 @@ export default function LandingPageClient() { const [username, setUsername] = useState(''); const [instantUsername, setInstantUsername] = useState(''); + const [generationKey, setGenerationKey] = useState(0); const [copied, setCopied] = useState(false); const [badgeResult, setBadgeResult] = useState<{ @@ -373,7 +374,9 @@ export default function LandingPageClient() { latestPreviewUsernameRef.current = previewUsername; }, [previewUsername]); - const badgeUrl = `/api/streak?user=${encodeURIComponent(previewUsername)}`; + const badgeUrl = `/api/streak?user=${encodeURIComponent(previewUsername)}${ + generationKey ? `&t=${generationKey}` : '' + }`; const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL ?? 'https://commitpulse.vercel.app').replace( /\/$/, '' @@ -581,13 +584,15 @@ export default function LandingPageClient() { setUsername(name); setInstantUsername(name); setBadgeResult(null); + setGenerationKey((prev) => prev + 1); }; const handleGenerate = (e: React.FormEvent) => { e.preventDefault(); - if (trimmedUsername.length > 0 && trimmedUsername !== previewUsername) { + if (trimmedUsername.length > 0) { setInstantUsername(trimmedUsername); setBadgeResult(null); + setGenerationKey((prev) => prev + 1); trackUser(trimmedUsername); addSearch(trimmedUsername); } diff --git a/app/page.test.tsx b/app/page.test.tsx index 5d6d08cfa..366a9874c 100644 --- a/app/page.test.tsx +++ b/app/page.test.tsx @@ -217,4 +217,68 @@ describe('LandingPage', () => { expect(global.fetch).toHaveBeenCalled(); }); }); + + it('re-triggers badge generation and resolves loading state on subsequent clicks', async () => { + vi.spyOn(global, 'fetch').mockImplementation((url) => { + if (typeof url === 'string' && url.includes('/api/user-details')) { + return Promise.resolve({ + ok: true, + json: () => + Promise.resolve({ + exists: true, + login: 'octocat', + name: 'The Octocat', + avatar_url: 'https://github.com/octocat.png', + public_repos: 10, + stats: { currentStreak: 5, longestStreak: 12, totalContributions: 150 }, + }), + } as Response); + } + return Promise.resolve({ ok: true, text: () => Promise.resolve('') } as Response); + }); + + render(); + + const input = screen.getByPlaceholderText('Enter GitHub Username') as HTMLInputElement; + + fireEvent.change(input, { + target: { value: 'octocat' }, + }); + + const generateButton = screen.getByRole('button', { + name: /Generate Badge/i, + }); + + // 1st click + await act(async () => { + fireEvent.click(generateButton); + }); + + let badgeImg = screen.getByTestId('badge-img') as HTMLImageElement; + expect(badgeImg).toBeInTheDocument(); + const initialSrc = badgeImg.src; + expect(initialSrc).toContain('/api/streak?user=octocat'); + + // Simulate initial onLoad + await act(async () => { + fireEvent.load(badgeImg); + }); + + // 2nd click with same username to re-trigger generation + await act(async () => { + fireEvent.click(generateButton); + }); + + badgeImg = screen.getByTestId('badge-img') as HTMLImageElement; + const retriggeredSrc = badgeImg.src; + expect(retriggeredSrc).toContain('/api/streak?user=octocat&t='); + + // Simulate second onLoad + await act(async () => { + fireEvent.load(badgeImg); + }); + + // Verify SVG export buttons appear after loaded + expect(screen.getByRole('button', { name: /Download SVG/i })).toBeInTheDocument(); + }); });