Skip to content

Commit 0a630e3

Browse files
test: verify buildCommitClock handles empty user metric logs (JhaSourav07#1688)
## Description Added a test case for `buildCommitClock([])` with empty user metric logs to verify the utility gracefully handles boundary inputs and bypasses writing undefined records to dashboard analytics. ## Testing - Ran `npx vitest run lib/github.test.ts` - Result: 73 passed ✅ Fixes JhaSourav07#1580 ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [ ] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [ ] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [ ] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
1 parent bd96dce commit 0a630e3

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

app/components/CustomizeCTA.test.tsx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,126 @@ describe('CustomizeCTA', () => {
123123
expect(decorativeIcon).toBeTruthy();
124124
});
125125
});
126+
127+
describe('responsive breakpoints', () => {
128+
it('renders all layout structure elements across viewports', () => {
129+
const { container } = render(<CustomizeCTA />);
130+
131+
// Container with customization-studio id
132+
const ctaSection = container.querySelector('#customization-studio');
133+
expect(ctaSection).toBeTruthy();
134+
135+
// Outer rounded card container
136+
const cardContainer = container.querySelector('[class*="rounded-[2rem]"]');
137+
expect(cardContainer).toBeTruthy();
138+
139+
// Inner flex container that applies responsive layout
140+
const flexContainer = container.querySelector('[class*="flex"]');
141+
expect(flexContainer).toBeTruthy();
142+
143+
// Text content area
144+
const textArea = container.querySelector('[class*="flex-1"]');
145+
expect(textArea).toBeTruthy();
146+
147+
// CTA button shrink wrapper
148+
const buttonWrapper = container.querySelector('[class*="shrink-0"]');
149+
expect(buttonWrapper).toBeTruthy();
150+
});
151+
152+
it('renders responsive text sizing from mobile to desktop', () => {
153+
const { container } = render(<CustomizeCTA />);
154+
155+
const heading = screen.getByRole('heading', { level: 2 });
156+
157+
// Check that heading has responsive classes (text-2xl for mobile, md:text-3xl for desktop)
158+
const headingClass = heading.getAttribute('class');
159+
expect(headingClass).toMatch(/text-2xl/);
160+
expect(headingClass).toMatch(/md:text-3xl/);
161+
});
162+
163+
it('applies responsive gap and flex direction across breakpoints', () => {
164+
const { container } = render(<CustomizeCTA />);
165+
166+
// Find the flex container with responsive gap and flex direction
167+
// gap-8 for consistent spacing, md:flex-row for desktop layout
168+
const flexContent = container.querySelector('[class*="flex"][class*="gap-8"]');
169+
expect(flexContent).toBeTruthy();
170+
171+
const flexClass = flexContent?.getAttribute('class') || '';
172+
expect(flexClass).toMatch(/gap-8/);
173+
expect(flexClass).toMatch(/md:flex-row/);
174+
expect(flexClass).toMatch(/flex-col/);
175+
});
176+
177+
it('ensures button styling is responsive across viewports', () => {
178+
const { container } = render(<CustomizeCTA />);
179+
180+
const button = container.querySelector('[class*="px-4"][class*="md:px-"]');
181+
expect(button).toBeTruthy();
182+
183+
const buttonClass = button?.getAttribute('class') || '';
184+
expect(buttonClass).toMatch(/px-4/);
185+
expect(buttonClass).toMatch(/md:px-7/);
186+
});
187+
188+
it('renders decorative background blobs that are hidden from interaction', () => {
189+
const { container } = render(<CustomizeCTA />);
190+
191+
const blobs = container.querySelectorAll('[class*="blur-"]');
192+
expect(blobs.length).toBeGreaterThanOrEqual(2);
193+
194+
blobs.forEach((blob) => {
195+
const blobClass = blob.getAttribute('class') || '';
196+
expect(blobClass).toMatch(/pointer-events-none/);
197+
});
198+
});
199+
200+
it('verifies navigation path is correct across all viewport sizes', () => {
201+
render(<CustomizeCTA />);
202+
203+
const link = screen.getByRole('link');
204+
expect(link.getAttribute('href')).toBe('/customize');
205+
206+
// Link should be visible and accessible regardless of viewport
207+
expect(link).toHaveProperty('textContent');
208+
expect(link.textContent).toContain('Open Customization Studio');
209+
});
210+
211+
it('renders heading text layout appropriate for mobile and desktop', () => {
212+
render(<CustomizeCTA />);
213+
214+
const eyebrowLabel = screen.getByText('Customization Studio');
215+
const eyebrowClass = eyebrowLabel.getAttribute('class') || '';
216+
217+
// Verify eyebrow styling is consistent
218+
expect(eyebrowClass).toMatch(/uppercase/);
219+
expect(eyebrowClass).toMatch(/text-xs/);
220+
221+
const heading = screen.getByRole('heading', { level: 2 });
222+
const headingClass = heading.getAttribute('class') || '';
223+
224+
// Heading has responsive text sizing
225+
expect(headingClass).toMatch(/text-2xl/);
226+
expect(headingClass).toMatch(/md:text-3xl/);
227+
228+
// Heading's parent container has responsive text alignment
229+
const parentContainer = heading.closest('[class*="flex-1"]');
230+
expect(parentContainer).toBeTruthy();
231+
232+
const parentClass = parentContainer?.getAttribute('class') || '';
233+
expect(parentClass).toMatch(/text-center/);
234+
expect(parentClass).toMatch(/md:text-left/);
235+
});
236+
237+
it('renders content sections with responsive text alignment', () => {
238+
const { container } = render(<CustomizeCTA />);
239+
240+
const contentArea = container.querySelector('[class*="text-center"][class*="md:text-left"]');
241+
expect(contentArea).toBeTruthy();
242+
243+
const contentClass = contentArea?.getAttribute('class') || '';
244+
expect(contentClass).toMatch(/text-center/);
245+
expect(contentClass).toMatch(/md:text-left/);
246+
});
247+
});
126248
});

lib/github.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,15 @@ describe('buildCommitClock', () => {
12791279
expect(result.every((item) => item.commits === 0)).toBe(true);
12801280
});
12811281

1282+
it('returns defined dashboard analytics rows when user metric logs are empty', () => {
1283+
const result = buildCommitClock([]);
1284+
1285+
expect(result).toHaveLength(7);
1286+
expect(result.every((item) => typeof item.day === 'string')).toBe(true);
1287+
expect(result.every((item) => typeof item.commits === 'number')).toBe(true);
1288+
expect(result.every((item) => item.commits === 0)).toBe(true);
1289+
});
1290+
12821291
it('always returns exactly 7 items', () => {
12831292
const result = buildCommitClock([{ date: '2024-01-07', contributionCount: 1 }]);
12841293

0 commit comments

Comments
 (0)