Skip to content

Commit 66a3672

Browse files
author
ChetanSenta
committed
fix(generatorConstants): add syncopate and spacegrotesk to FONT_MAP to prevent duplicate Google Fonts @import
1 parent 2e604a1 commit 66a3672

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

lib/svg/generatorConstants.test.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { describe, it, expect } from 'vitest';
22
import { SVG_WIDTH, SVG_HEIGHT, isFontKey } from './generatorConstants';
33
import { FONT_MAP } from './fonts';
4+
import { generateSVG } from './generator';
5+
import type { BadgeParams } from '../../types';
46

57
describe('generatorConstants', () => {
68
it('SVG_WIDTH equals 600', () => {
@@ -39,3 +41,149 @@ describe('generatorConstants', () => {
3941
expect(isFontKey('')).toBe(false);
4042
});
4143
});
44+
45+
describe('FONT_MAP — previously missing bundled font entries', () => {
46+
it('contains syncopate — prevents duplicate @import for the design system title font', () => {
47+
expect(FONT_MAP).toHaveProperty('syncopate');
48+
expect(FONT_MAP['syncopate']).toBe('"Syncopate", sans-serif');
49+
});
50+
51+
it('contains spacegrotesk — prevents duplicate @import for the design system stats font', () => {
52+
expect(FONT_MAP).toHaveProperty('spacegrotesk');
53+
expect(FONT_MAP['spacegrotesk']).toBe('"Space Grotesk", sans-serif');
54+
});
55+
56+
it('contains space grotesk (with space) — handles user input with a space', () => {
57+
expect(FONT_MAP).toHaveProperty('space grotesk');
58+
expect(FONT_MAP['space grotesk']).toBe('"Space Grotesk", sans-serif');
59+
});
60+
61+
it('contains firacode alias — handles ?font=firacode as well as ?font=fira', () => {
62+
expect(FONT_MAP).toHaveProperty('firacode');
63+
expect(FONT_MAP['firacode']).toBe('"Fira Code", monospace');
64+
});
65+
66+
it('syncopate and spacegrotesk map to sans-serif stack — not monospace', () => {
67+
expect(FONT_MAP['syncopate']).toContain('sans-serif');
68+
expect(FONT_MAP['spacegrotesk']).toContain('sans-serif');
69+
});
70+
});
71+
72+
describe('FONT_MAP — SVG output regression: no duplicate @import for bundled fonts', () => {
73+
it('font=syncopate does not generate a dynamic Google Fonts @import', () => {
74+
const svg = generateSVG(
75+
{
76+
currentStreak: 5,
77+
longestStreak: 10,
78+
totalContributions: 100,
79+
todayDate: '2024-06-12',
80+
},
81+
{ user: 'chetan', font: 'syncopate' } as unknown as BadgeParams,
82+
{
83+
totalContributions: 100,
84+
weeks: [
85+
{
86+
contributionDays: [{ contributionCount: 5, date: '2024-06-12' }],
87+
},
88+
],
89+
}
90+
);
91+
92+
// Count how many times Syncopate appears in @import statements
93+
const importMatches = [...svg.matchAll(/@import url\([^)]*Syncopate[^)]*\)/gi)];
94+
// Must appear exactly once (the unconditional bundled import)
95+
// Before the fix it appeared twice — this is the regression guard
96+
expect(importMatches.length).toBe(1);
97+
});
98+
99+
it('font=spacegrotesk does not generate a dynamic Google Fonts @import', () => {
100+
const svg = generateSVG(
101+
{
102+
currentStreak: 5,
103+
longestStreak: 10,
104+
totalContributions: 100,
105+
todayDate: '2024-06-12',
106+
},
107+
{ user: 'chetan', font: 'spacegrotesk' } as unknown as BadgeParams,
108+
{
109+
totalContributions: 100,
110+
weeks: [
111+
{
112+
contributionDays: [{ contributionCount: 5, date: '2024-06-12' }],
113+
},
114+
],
115+
}
116+
);
117+
118+
const importMatches = [...svg.matchAll(/@import url\([^)]*Space\+Grotesk[^)]*\)/gi)];
119+
// Must appear exactly once — not twice
120+
expect(importMatches.length).toBe(1);
121+
});
122+
123+
it('font=Inter still generates a dynamic @import (non-bundled font — correct behavior)', () => {
124+
const svg = generateSVG(
125+
{
126+
currentStreak: 5,
127+
longestStreak: 10,
128+
totalContributions: 100,
129+
todayDate: '2024-06-12',
130+
},
131+
{ user: 'chetan', font: 'Inter' } as unknown as BadgeParams,
132+
{
133+
totalContributions: 100,
134+
weeks: [
135+
{
136+
contributionDays: [{ contributionCount: 5, date: '2024-06-12' }],
137+
},
138+
],
139+
}
140+
);
141+
142+
// Inter is NOT in the unconditional @import — dynamic fetch is correct here
143+
expect(svg).toContain('family=Inter');
144+
});
145+
146+
it('font=syncopate resolves to Syncopate CSS font-family in style block', () => {
147+
const svg = generateSVG(
148+
{
149+
currentStreak: 5,
150+
longestStreak: 10,
151+
totalContributions: 100,
152+
todayDate: '2024-06-12',
153+
},
154+
{ user: 'chetan', font: 'syncopate' } as unknown as BadgeParams,
155+
{
156+
totalContributions: 100,
157+
weeks: [
158+
{
159+
contributionDays: [{ contributionCount: 5, date: '2024-06-12' }],
160+
},
161+
],
162+
}
163+
);
164+
165+
expect(svg).toContain('font-family: "Syncopate", sans-serif');
166+
});
167+
168+
it('font=spacegrotesk resolves to Space Grotesk CSS font-family in style block', () => {
169+
const svg = generateSVG(
170+
{
171+
currentStreak: 5,
172+
longestStreak: 10,
173+
totalContributions: 100,
174+
todayDate: '2024-06-12',
175+
},
176+
{ user: 'chetan', font: 'spacegrotesk' } as unknown as BadgeParams,
177+
{
178+
totalContributions: 100,
179+
weeks: [
180+
{
181+
contributionDays: [{ contributionCount: 5, date: '2024-06-12' }],
182+
},
183+
],
184+
}
185+
);
186+
187+
expect(svg).toContain('font-family: "Space Grotesk", sans-serif');
188+
});
189+
});

0 commit comments

Comments
 (0)