Skip to content

Commit 43af4a9

Browse files
authored
test(RepositoryGraph): empty/missing inputs edge cases (JhaSourav07#2603) (JhaSourav07#3617)
## Description Fixes JhaSourav07#2603 Added a new isolated test file `components/dashboard/RepositoryGraph.empty-fallback.test.tsx` covering **Edge Cases & Empty/Missing Inputs Verification (Variation 1)** for the `RepositoryGraph` component. **What's covered (5 test cases):** - Renders fallback UI when `nodes` array is completely empty - Renders fallback UI when only a single User node is provided (`nodes.length <= 1`) - Verifies fallback container has correct DOM structure and styling markers (`border-dashed`, `rounded-xl`, Box icon) - Ensures no runtime errors or hydration failures with missing/empty links - Confirms fallback UI stays stable even with a single non-User node **Test results:** ✓ Test Files 1 passed (1) ✓ Tests 5 passed (5) Run locally with: `npx vitest run components/dashboard/RepositoryGraph.empty-fallback.test.tsx` ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview _N/A — this PR only adds a unit test file for the `RepositoryGraph` component. No UI changes._ ## 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): ...`). - [x] I have updated `README.md` if I added a new theme or URL parameter. - [ ] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [x] 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.
2 parents 56710ad + f205052 commit 43af4a9

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { describe, it, expect, vi, beforeEach } from 'vitest';
3+
import { render, screen } from '@testing-library/react';
4+
import React from 'react';
5+
import RepositoryGraph from './RepositoryGraph';
6+
import type { GraphNode, GraphLink } from '@/types';
7+
8+
// Mock next/dynamic so ForceGraph2D renders synchronously and never touches the real canvas library
9+
vi.mock('next/dynamic', () => {
10+
const DynamicForceGraphMock = React.forwardRef((props: any, ref: any) => {
11+
React.useImperativeHandle(ref, () => ({
12+
centerAt: vi.fn(),
13+
zoom: vi.fn(),
14+
}));
15+
16+
return (
17+
<div data-testid="force-graph-2d">
18+
{props.graphData?.nodes?.map((node: any) => (
19+
<span key={node.id} data-testid={`graph-node-${node.id}`}>
20+
{node.name}
21+
</span>
22+
))}
23+
</div>
24+
);
25+
});
26+
DynamicForceGraphMock.displayName = 'ForceGraph2D';
27+
return {
28+
default: () => DynamicForceGraphMock,
29+
};
30+
});
31+
32+
// Make container dimensions deterministic in jsdom
33+
Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
34+
configurable: true,
35+
value: 800,
36+
});
37+
38+
describe('RepositoryGraph - Edge Cases & Empty/Missing Inputs (Variation 1)', () => {
39+
beforeEach(() => {
40+
vi.clearAllMocks();
41+
});
42+
43+
it('renders the fallback UI when nodes array is completely empty', () => {
44+
const emptyData = { nodes: [] as GraphNode[], links: [] as GraphLink[] };
45+
render(<RepositoryGraph data={emptyData} />);
46+
47+
// Fallback message must be visible
48+
expect(screen.getByText('No repository relationship data available yet.')).toBeDefined();
49+
50+
// The actual force graph must NOT render
51+
expect(screen.queryByTestId('force-graph-2d')).toBeNull();
52+
});
53+
54+
it('renders the fallback UI when only a single User node is provided (nodes.length <= 1)', () => {
55+
const singleNodeData = {
56+
nodes: [
57+
{ id: 'user1', name: 'Solo User', type: 'User', val: 30, color: '#FFF' },
58+
] as GraphNode[],
59+
links: [] as GraphLink[],
60+
};
61+
render(<RepositoryGraph data={singleNodeData} />);
62+
63+
expect(screen.getByText('No repository relationship data available yet.')).toBeDefined();
64+
// Header of the full graph view should NOT appear in fallback state
65+
expect(screen.queryByText('🌐 Repository Dependency Graph')).toBeNull();
66+
});
67+
68+
it('renders the fallback container with the correct DOM structure and styling markers', () => {
69+
const emptyData = { nodes: [] as GraphNode[], links: [] as GraphLink[] };
70+
const { container } = render(<RepositoryGraph data={emptyData} />);
71+
72+
const fallback = container.querySelector('div.border-dashed');
73+
// Key DOM markers exist for the empty/fallback state
74+
expect(fallback).not.toBeNull();
75+
expect(fallback?.className).toContain('rounded-xl');
76+
expect(fallback?.className).toContain('flex');
77+
78+
// The Box icon (lucide-react) should be rendered inside fallback as an svg
79+
const icon = fallback?.querySelector('svg');
80+
expect(icon).not.toBeNull();
81+
});
82+
83+
it('does not throw runtime errors or hydration failures with missing/empty links', () => {
84+
const dataWithMissingLinks = {
85+
nodes: [] as GraphNode[],
86+
links: [] as GraphLink[],
87+
};
88+
89+
// The render itself must complete without throwing
90+
expect(() => render(<RepositoryGraph data={dataWithMissingLinks} />)).not.toThrow();
91+
expect(screen.getByText('No repository relationship data available yet.')).toBeDefined();
92+
});
93+
94+
it('keeps fallback UI stable even when nodes contain a single non-User node (still <= 1)', () => {
95+
const singleRepoData = {
96+
nodes: [
97+
{
98+
id: 'repo-only',
99+
name: 'Lonely Repo',
100+
type: 'Repo',
101+
val: 10,
102+
color: '#FFF',
103+
stats: { stars: 0, forks: 0 },
104+
},
105+
] as GraphNode[],
106+
links: [] as GraphLink[],
107+
};
108+
109+
render(<RepositoryGraph data={singleRepoData} />);
110+
111+
// Fallback should still show since nodes.length <= 1
112+
expect(screen.getByText('No repository relationship data available yet.')).toBeDefined();
113+
// No interactive filter buttons should be rendered in fallback state
114+
expect(screen.queryByText('Personal')).toBeNull();
115+
expect(screen.queryByText('Contributions')).toBeNull();
116+
expect(screen.queryByText('Forks')).toBeNull();
117+
});
118+
});

0 commit comments

Comments
 (0)