diff --git a/lib/rss.test.ts b/lib/rss.test.ts new file mode 100644 index 000000000..8b89c725b --- /dev/null +++ b/lib/rss.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect, vi } from 'vitest'; +import { fetchLatestArticles } from './rss'; + +// Mock rss-parser so it doesn't make real network requests during tests +vi.mock('rss-parser', () => { + return { + default: class MockParser { + parseURL = vi.fn().mockResolvedValue({ + items: [ + { + title: 'Test Post', + link: 'https://dev.to/test', + pubDate: '2026-06-15T00:00:00Z', + }, + ], + }); + }, + }; +}); + +describe('rss / fetchLatestArticles', () => { + it('[Bug fix] formats pubDate deterministically regardless of Node process locale', async () => { + // Because rss-parser is mocked above, this will reliably return the mock item + const articles = await fetchLatestArticles('devto', 'testuser'); + + expect(articles).toHaveLength(1); + // This verifies the 'en-US' Date locale change correctly generates "Jun 15, 2026" + expect(articles[0].pubDate).toBe('Jun 15, 2026'); + }); +}); diff --git a/lib/rss.ts b/lib/rss.ts index c4b972fe3..02de621ec 100644 --- a/lib/rss.ts +++ b/lib/rss.ts @@ -40,8 +40,12 @@ export async function fetchLatestArticles( const articles = feed.items.slice(0, 3).map((item) => ({ title: item.title || 'Untitled', link: item.link || '', + // Explicit 'en-US' locale, matching the pattern already used in + // lib/github.ts's joinedDate formatting — this string is baked + // server-side into the generated SVG, so it must be deterministic + // regardless of the server's ambient runtime locale/ICU build. pubDate: item.pubDate - ? new Date(item.pubDate).toLocaleDateString(undefined, { + ? new Date(item.pubDate).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', diff --git a/lib/svg/repoSpotlight.test.ts b/lib/svg/repoSpotlight.test.ts new file mode 100644 index 000000000..d362b26fa --- /dev/null +++ b/lib/svg/repoSpotlight.test.ts @@ -0,0 +1,32 @@ +import { describe, it, expect } from 'vitest'; +import { generateRepoSpotlightSVG } from './repoSpotlight'; +import { GitHubRepo } from '@/lib/github'; +import { BadgeParams } from '@/types'; + +describe('generateRepoSpotlightSVG', () => { + it('[Bug fix] formats pushed_at with a fixed, explicit locale', () => { + // Construct a minimal mocked repository to satisfy the function requirements + const mockRepo = { + name: 'test-repo', + description: 'A test repository', + language: 'TypeScript', + stargazers_count: 100, + forks_count: 50, + pushed_at: '2026-06-15T00:00:00Z', + participation: [0, 5, 10, 5, 0], + } as GitHubRepo; + + // Provide default mockup formatting arguments + const mockParams = { + bg: 'ffffff', + text: '000000', + accent: 'ff0000', + radius: 4, + } as BadgeParams; + + const svg = generateRepoSpotlightSVG(mockRepo, mockParams); + + // Check that our forced date outputs perfectly in the generated SVG + expect(svg).toContain('Updated Jun 15, 2026'); + }); +}); diff --git a/lib/svg/repoSpotlight.ts b/lib/svg/repoSpotlight.ts index 10f9e94d2..23ca6923a 100644 --- a/lib/svg/repoSpotlight.ts +++ b/lib/svg/repoSpotlight.ts @@ -58,7 +58,8 @@ export function generateRepoSpotlightSVG(repo: GitHubRepo, params: BadgeParams): // Parse dates if available const dateStr = repo.pushed_at - ? new Date(repo.pushed_at).toLocaleDateString(undefined, { + ? // Explicit 'en-US' locale — see lib/rss.ts for the same fix and rationale. + new Date(repo.pushed_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', diff --git a/utils/dateHelpers.test.ts b/utils/dateHelpers.test.ts index 7a8797337..ab7a185a3 100644 --- a/utils/dateHelpers.test.ts +++ b/utils/dateHelpers.test.ts @@ -3,6 +3,7 @@ import { getAuthorLocalHour, getViewerLocalHour, processCommitTimestamps } from describe('dateHelpers', () => { describe('getAuthorLocalHour', () => { + // ... keeping existing tests identical ... it('extracts hour from a standard ISO timestamp with positive offset', () => { expect(getAuthorLocalHour('2024-03-10T15:30:00+02:00')).toBe(15); }); @@ -70,9 +71,6 @@ describe('dateHelpers', () => { expect(result).toEqual({ morning: 0, afternoon: 0, evening: 0, night: 0 }); }); - // NOTE: Added 'Z' to timestamp strings below to explicitly parse them as UTC, - // which aligns with processCommitTimestamps using getUTCHours() internally. - it('counts valid morning commits correctly', () => { const result = processCommitTimestamps(['2024-03-10T09:00:00Z', '2024-03-10T11:30:00Z']); expect(result.morning).toBe(2); @@ -109,8 +107,6 @@ describe('dateHelpers', () => { expect(result.evening).toBe(0); }); - // Regression test: verifies timezone-agnostic behavior. - // This test would fail with getHours() in IST but passes with getUTCHours(). it('produces consistent results regardless of system timezone', () => { const timestamps = [ '2024-03-10T09:00:00Z', // 09:00 UTC = morning