From 2f2a8822edf2310e1ade896095f704934ff7e1ee Mon Sep 17 00:00:00 2001 From: Mayank Singh Rawat Date: Thu, 4 Jun 2026 05:00:27 +0530 Subject: [PATCH] test(DashboardClient): add empty fallback coverage --- .../DashboardClient.empty-fallback.test.tsx | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 components/dashboard/DashboardClient.empty-fallback.test.tsx diff --git a/components/dashboard/DashboardClient.empty-fallback.test.tsx b/components/dashboard/DashboardClient.empty-fallback.test.tsx new file mode 100644 index 000000000..e00aaad79 --- /dev/null +++ b/components/dashboard/DashboardClient.empty-fallback.test.tsx @@ -0,0 +1,224 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; +import DashboardClient from './DashboardClient'; + +vi.mock('next/navigation', () => ({ + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + refresh: vi.fn(), + }), +})); + +vi.mock('sonner', () => ({ + toast: { + success: vi.fn(), + error: vi.fn(), + info: vi.fn(), + }, +})); + +vi.mock('framer-motion', () => ({ + motion: { + div: ({ children, className, style, ...props }: any) => { + delete props.initial; + delete props.animate; + delete props.whileInView; + delete props.viewport; + delete props.transition; + delete props.whileHover; + + return ( +
+ {children} +
+ ); + }, + }, + AnimatePresence: ({ children }: any) => children, +})); + +vi.mock('./RefreshButton', () => ({ + default: () => , +})); + +vi.mock('./ProfileCard', () => ({ + default: ({ user }: any) => ( +
+
{user.name}
+
+ ), +})); + +vi.mock('./Achievements', () => ({ + default: () =>
Achievements
, +})); + +vi.mock('./ActivityLandscape', () => ({ + default: () =>
Activity Landscape
, +})); + +vi.mock('./LanguageChart', () => ({ + default: () =>
Language Chart
, +})); + +vi.mock('./CommitClock', () => ({ + default: () =>
Commit Clock
, +})); + +vi.mock('./Heatmap', () => ({ + default: () =>
Heatmap
, +})); + +vi.mock('./HistoricalTrendView', () => ({ + default: () =>
Historical Trend
, +})); + +vi.mock('./AIInsights', () => ({ + default: () =>
AI Insights
, +})); + +vi.mock('./StatsCard', () => ({ + default: ({ title }: any) =>
{title}
, +})); + +vi.mock('./RepositoryGraph', () => ({ + default: () =>
Repository Graph
, +})); + +vi.mock('./PopularRepos', () => ({ + PopularRepos: () =>
Popular Repos
, +})); + +vi.mock('./ResumeProfileSection', () => ({ + default: () =>
Resume Profile
, +})); + +vi.mock('./ProfileOptimizerModal', () => ({ + default: () =>
, +})); + +vi.mock('./ComparisonStatsCard', () => ({ + default: ({ title }: any) =>
{title}
, +})); + +vi.mock('./RadarChart', () => ({ + default: () =>
Radar Chart
, +})); + +vi.mock('./GrowthTrendChart', () => ({ + default: () =>
Growth Trend Chart
, +})); + +const mockPeriod = { + kind: 'year' as const, + label: '2026', + from: '2026-01-01T00:00:00.000Z', + to: '2026-12-31T23:59:59.999Z', + year: '2026', +}; + +const baseInitialData = { + profile: { + username: 'Mayank2905', + name: 'Mayank', + avatarUrl: 'https://avatars.githubusercontent.com/u/1', + isPro: false, + bio: 'Software Developer', + location: 'India', + joinedDate: '2020-01-01', + developerScore: 85, + stats: { + repositories: 30, + followers: 120, + following: 80, + stars: 45, + }, + }, + stats: { + currentStreak: 5, + peakStreak: 35, + totalContributions: 600, + }, + languages: [ + { name: 'TypeScript', color: '#3178c6', percentage: 60 }, + { name: 'Python', color: '#3572A5', percentage: 40 }, + ], + activity: [ + { date: '2026-05-01', count: 5, intensity: 1 as const }, + { date: '2026-05-02', count: 0, intensity: 0 as const }, + ], + insights: [{ id: '1', icon: 'zap', text: 'Highly active' }], + achievements: [], + commitClock: [], + graphData: { nodes: [], links: [] }, + popularRepos: [], + pinnedRepos: [], +}; + +const renderDashboard = (overrides = {}) => { + return render( + + ); +}; + +describe('DashboardClient empty fallback states', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('renders with an empty languages array without crashing', () => { + renderDashboard({ languages: [] }); + + expect(screen.getByTestId('profile-card')).toBeDefined(); + expect(screen.getByTestId('language-chart')).toBeDefined(); + }); + + it('renders with an empty activity array without crashing', () => { + renderDashboard({ activity: [] }); + + expect(screen.getByTestId('activity-landscape')).toBeDefined(); + expect(screen.getByTestId('historical-trend-view')).toBeDefined(); + }); + + it('renders with an empty insights array without runtime errors', () => { + renderDashboard({ insights: [] }); + + expect(screen.getByTestId('ai-insights')).toBeDefined(); + }); + + it('renders with an empty achievements array and preserves layout', () => { + renderDashboard({ achievements: [] }); + + expect(screen.getByTestId('achievements')).toBeDefined(); + expect(screen.getByTestId('resume-profile-section')).toBeDefined(); + }); + + it('renders all core dashboard sections when multiple collections are empty', () => { + renderDashboard({ + languages: [], + activity: [], + insights: [], + achievements: [], + commitClock: [], + popularRepos: [], + pinnedRepos: [], + graphData: { nodes: [], links: [] }, + }); + + expect(screen.getByTestId('profile-card')).toBeDefined(); + expect(screen.getByTestId('achievements')).toBeDefined(); + expect(screen.getByTestId('activity-landscape')).toBeDefined(); + expect(screen.getByTestId('language-chart')).toBeDefined(); + expect(screen.getByTestId('commit-clock')).toBeDefined(); + expect(screen.getByTestId('historical-trend-view')).toBeDefined(); + expect(screen.getByTestId('ai-insights')).toBeDefined(); + expect(screen.getByTestId('popular-repos')).toBeDefined(); + expect(screen.getByTestId('repository-graph')).toBeDefined(); + }); +});