Skip to content

Commit a57292a

Browse files
Merge branch 'main' into enhance-footer-hover-effects
2 parents cbecfce + d67b08e commit a57292a

62 files changed

Lines changed: 4042 additions & 362 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

THEMES.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ https://commitpulse.vercel.app/api/streak?user=YOUR_USERNAME&theme=<slug>
3232
| nord_light | `#eceff4` | `#2e3440` | `#5e81ac` |
3333
| obsidian | `#1a1a2e` | `#e2e8f0` | `#f59e0b` |
3434
| cyber-pulse | `#000000` | `#ffffff` | `#00ffee` |
35+
| tokyonight | `#1a1b26` | `#c0caf5` | `#f7768e` |
36+
| cyberpunk | `#fce22a` | `#111111` | `#ff003c` |
3537

3638
---
3739

@@ -277,6 +279,30 @@ https://commitpulse.vercel.app/api/streak?user=YOUR_USERNAME&theme=<slug>
277279

278280
---
279281

282+
### Tokyo Night
283+
284+
![tokyonight](https://commitpulse.vercel.app/api/streak?user=jhasourav07&theme=tokyonight)
285+
286+
| Parameter | Value |
287+
| --------- | ------ |
288+
| `bg` | 1a1b26 |
289+
| `text` | c0caf5 |
290+
| `accent` | f7768e |
291+
292+
---
293+
294+
### Cyberpunk
295+
296+
![cyberpunk](https://commitpulse.vercel.app/api/streak?user=jhasourav07&theme=cyberpunk)
297+
298+
| Parameter | Value |
299+
| --------- | ------ |
300+
| `bg` | fce22a |
301+
| `text` | 111111 |
302+
| `accent` | ff003c |
303+
304+
---
305+
280306
## Custom Theme
281307

282308
Not finding what you want? Build your own using raw color parameters - all values are hex codes **without** the `#` prefix:

app/api/streak/route.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,24 @@ describe('GET /api/streak', () => {
277277
expect(fetchGitHubContributions).not.toHaveBeenCalled();
278278
});
279279

280+
it('returns 400 when grace is below the minimum value', async () => {
281+
const response = await GET(
282+
makeRequest({
283+
user: 'octocat',
284+
grace: '-1',
285+
})
286+
);
287+
288+
expect(response.status).toBe(400);
289+
290+
const body = await response.json();
291+
292+
expect(body.error).toBe('Invalid parameters');
293+
expect(body.details.fieldErrors.grace[0]).toBe('grace must be an integer between 0 and 7');
294+
295+
expect(fetchGitHubContributions).not.toHaveBeenCalled();
296+
});
297+
280298
it('returns 400 for unsupported ?layout query parameter values (strict schema validation)', async () => {
281299
const response = await GET(
282300
new Request('http://localhost:3000/api/streak?user=octocat&layout=unsupported_layout')

app/api/wrapped/route.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const mockWrappedStats: WrappedStats = {
3030
busiestMonth: '2025-11',
3131
weekendRatio: 24,
3232
topLanguage: 'TypeScript',
33+
calendar: mockCalendar,
3334
};
3435

3536
function makeRequest(params: Record<string, string> = {}): Request {

app/api/wrapped/route.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// app/api/wrapped/route.ts
22

33
import { NextResponse } from 'next/server';
4-
import { getWrappedData, fetchGitHubContributions } from '@/lib/github';
4+
import { getWrappedData } from '@/lib/github';
55
import { generateWrappedSVG, generateNotFoundSVG, generateRateLimitSVG } from '@/lib/svg/generator';
66
import { wrappedParamsSchema } from '@/lib/validations';
77
import type { BadgeParams } from '@/types';
@@ -90,15 +90,10 @@ export async function GET(request: Request) {
9090
scale: 'linear',
9191
};
9292

93-
// Fetch the wrapped stats for the year
93+
// Fetch the wrapped stats for the year (calendar is included to avoid a duplicate API call)
9494
const wrappedStats = await getWrappedData(user, year, { bypassCache: refresh });
9595

96-
// Fetch calendar contributions for rendering the background mini-monolith
97-
const from = `${year}-01-01T00:00:00Z`;
98-
const to = `${year}-12-31T23:59:59Z`;
99-
const { calendar } = await fetchGitHubContributions(user, { from, to, bypassCache: refresh });
100-
101-
const svg = generateWrappedSVG(wrappedStats, params, year, calendar);
96+
const svg = generateWrappedSVG(wrappedStats, params, year, wrappedStats.calendar);
10297

10398
// Cache-Control: Annual wrapped stats are stable, cache for 24 hours.
10499
// Clients can bust with ?refresh=true.

app/api/wrapped/tests/statsCalculation.test.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe('GET /api/wrapped stats calculation', () => {
4646
vi.clearAllMocks();
4747

4848
const wrappedStats: WrappedStats = {
49+
calendar: statsCalendar,
4950
...calculateWrappedStats(statsCalendar),
5051
topLanguage: 'TypeScript',
5152
};
@@ -87,12 +88,6 @@ describe('GET /api/wrapped stats calculation', () => {
8788
expect(getWrappedData).toHaveBeenCalledWith('octocat', '2025', {
8889
bypassCache: false,
8990
});
90-
91-
expect(fetchGitHubContributions).toHaveBeenCalledWith('octocat', {
92-
from: '2025-01-01T00:00:00Z',
93-
to: '2025-12-31T23:59:59Z',
94-
bypassCache: false,
95-
});
9691
});
9792

9893
it('returns 400 and skips wrapped stats calculation when validation fails', async () => {

app/api/wrapped/tests/validation.test.ts

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,13 @@ describe('GET /api/wrapped validation', () => {
1414

1515
vi.mocked(getWrappedData).mockResolvedValue({
1616
totalContributions: 1500,
17-
daysActive: 300,
18-
longestStreak: 45,
19-
weekendCommits: 200,
17+
mostActiveDate: '2023-10-15',
18+
highestDailyCount: 50,
2019
busiestMonth: '2023-10',
21-
topRepositories: [{ name: 'commitpulse', count: 100 }],
22-
mostActiveDay: { date: '2023-10-15', contributionCount: 50 },
23-
streakStats: {
24-
currentStreak: 10,
25-
longestStreak: 45,
26-
totalContributions: 1500,
27-
todayDate: '2023-12-31',
28-
},
29-
} as unknown as import('@/types/dashboard').WrappedStats);
20+
weekendRatio: 13,
21+
topLanguage: 'TypeScript',
22+
calendar: { totalContributions: 1500, weeks: [] },
23+
});
3024

3125
vi.mocked(fetchGitHubContributions).mockResolvedValue({
3226
calendar: { totalContributions: 1500, weeks: [] },
@@ -58,11 +52,9 @@ describe('GET /api/wrapped validation', () => {
5852
const req = makeMockRequest({ user: 'octocat', theme: 'invalid_theme_name_123' });
5953
const res = await GET(req);
6054

61-
// The wrapped endpoint's zod schema transforms invalid themes to 'dark' gracefully.
6255
expect(res.status).toBe(200);
6356
const text = await res.text();
6457
expect(text).toContain('<svg');
65-
// Verify it falls back to the dark theme background
6658
expect(text).toContain('0d1117');
6759
});
6860

@@ -72,41 +64,25 @@ describe('GET /api/wrapped validation', () => {
7264

7365
expect(res.status).toBe(200);
7466
expect(getWrappedData).toHaveBeenCalledWith('octocat', '2023', { bypassCache: false });
75-
expect(fetchGitHubContributions).toHaveBeenCalledWith('octocat', {
76-
from: '2023-01-01T00:00:00Z',
77-
to: '2023-12-31T23:59:59Z',
78-
bypassCache: false,
79-
});
8067
});
8168

8269
it('TestCase 4: verifies computed stats metrics like streak peak and weekend commits in SVG output', async () => {
83-
// Setup mock to return specific metrics that will be rendered into the SVG
8470
vi.mocked(getWrappedData).mockResolvedValue({
8571
totalContributions: 5000,
86-
daysActive: 365,
87-
longestStreak: 45, // Streak peak
88-
weekendCommits: 200, // Weekend commits
72+
mostActiveDate: '2023-10-15',
73+
highestDailyCount: 50,
8974
busiestMonth: '2023-10',
90-
topRepositories: [{ name: 'commitpulse', count: 100 }],
91-
mostActiveDay: { date: '2023-10-15', contributionCount: 50 },
92-
streakStats: {
93-
currentStreak: 10,
94-
longestStreak: 45,
95-
totalContributions: 5000,
96-
todayDate: '2023-12-31',
97-
},
98-
} as unknown as import('@/types/dashboard').WrappedStats);
75+
weekendRatio: 4,
76+
topLanguage: 'TypeScript',
77+
calendar: { totalContributions: 5000, weeks: [] },
78+
});
9979

10080
const req = makeMockRequest({ user: 'octocat' });
10181
const res = await GET(req);
10282

10383
expect(res.status).toBe(200);
10484
const text = await res.text();
105-
10685
expect(text).toContain('<svg');
107-
// The generator should output the peak streak and weekend commits somewhere in the SVG.
108-
expect(text).toContain('45');
109-
expect(text).toContain('200');
11086
});
11187

11288
it('TestCase 5: ensures SVG visual components render correctly based on active params', async () => {
@@ -115,9 +91,7 @@ describe('GET /api/wrapped validation', () => {
11591

11692
expect(res.status).toBe(200);
11793
const text = await res.text();
118-
11994
expect(text).toContain('<svg');
120-
// Ensure the background color and radius are passed properly and rendered in the SVG
12195
expect(text).toContain('ff0000');
12296
expect(text).toContain('20');
12397
});

app/api/wrapped/yearBoundary.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const mockCalendar: ContributionCalendar = {
2424
};
2525

2626
const mockWrappedStats: WrappedStats = {
27+
calendar: mockCalendar,
2728
totalContributions: 1420,
2829
mostActiveDate: '2023-11-20',
2930
highestDailyCount: 42,
@@ -72,11 +73,6 @@ describe('yearBoundary constraints', () => {
7273
expect(response.status).toBe(200);
7374

7475
expect(getWrappedData).toHaveBeenCalledWith('octocat', '2023', { bypassCache: false });
75-
expect(fetchGitHubContributions).toHaveBeenCalledWith('octocat', {
76-
from: '2023-01-01T00:00:00Z',
77-
to: '2023-12-31T23:59:59Z',
78-
bypassCache: false,
79-
});
8076
});
8177

8278
it('Computed Stats Metrics: Verifies peak commits and weekend ratio render in the SVG output', async () => {

0 commit comments

Comments
 (0)