Skip to content

Commit d948a97

Browse files
authored
test: Add comprehensive test coverage for Reports components (#223)
* test: add comprehensive test coverage for Reports components - Add ReportsView.test.tsx with 18 tests covering data calculation, date filtering, and edge cases - Add ReportChart.test.tsx with 18 tests for rendering, chart elements, and export functionality - Add report-download-utils.test.ts with 11 tests for CSV/PNG export utilities - Achieve 100% coverage on ReportsView, ReportChart, and report-download-utils - Improve tasks-utils coverage from 39.56% to 94.5% - Increase overall Tasks directory coverage from 41.85% to 61.05% - All 214 tests passing with no regressions * test: remove duplicate test in ReportChart.test.tsx Remove duplicate 're-enables button after export completes' test case
1 parent 7a9a984 commit d948a97

3 files changed

Lines changed: 713 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2+
import { ReportChart } from '../ReportChart';
3+
import * as downloadUtils from '../report-download-utils';
4+
5+
jest.mock('recharts', () => ({
6+
ResponsiveContainer: ({ children }: any) => (
7+
<div data-testid="responsive-container">{children}</div>
8+
),
9+
BarChart: ({ children, data }: any) => (
10+
<div data-testid="bar-chart" data-chart-data={JSON.stringify(data)}>
11+
{children}
12+
</div>
13+
),
14+
Bar: ({ dataKey, fill, name }: any) => (
15+
<div data-testid={`bar-${dataKey}`} data-fill={fill} data-name={name} />
16+
),
17+
XAxis: ({ dataKey }: any) => <div data-testid="x-axis" data-key={dataKey} />,
18+
YAxis: () => <div data-testid="y-axis" />,
19+
Tooltip: () => <div data-testid="tooltip" />,
20+
Legend: () => <div data-testid="legend" />,
21+
CartesianGrid: () => <div data-testid="cartesian-grid" />,
22+
}));
23+
24+
jest.mock('../report-download-utils', () => ({
25+
exportReportToCSV: jest.fn(),
26+
exportChartToPNG: jest.fn().mockResolvedValue(undefined),
27+
}));
28+
29+
describe('ReportChart', () => {
30+
const mockData = [{ name: 'Today', completed: 5, ongoing: 3 }];
31+
32+
const defaultProps = {
33+
data: mockData,
34+
title: 'Daily Report',
35+
chartId: 'daily-report-chart',
36+
};
37+
38+
beforeEach(() => {
39+
jest.clearAllMocks();
40+
});
41+
42+
describe('Rendering', () => {
43+
it('renders chart with title', () => {
44+
render(<ReportChart {...defaultProps} />);
45+
expect(screen.getByText('Daily Report')).toBeInTheDocument();
46+
});
47+
48+
it('renders CSV export button', () => {
49+
render(<ReportChart {...defaultProps} />);
50+
const csvButton = screen.getByTitle('Download as CSV');
51+
expect(csvButton).toBeInTheDocument();
52+
});
53+
54+
it('renders PNG export button', () => {
55+
render(<ReportChart {...defaultProps} />);
56+
const pngButton = screen.getByTitle('Download as PNG');
57+
expect(pngButton).toBeInTheDocument();
58+
});
59+
60+
it('renders bar chart with data', () => {
61+
render(<ReportChart {...defaultProps} />);
62+
const barChart = screen.getByTestId('bar-chart');
63+
expect(barChart).toBeInTheDocument();
64+
expect(barChart.getAttribute('data-chart-data')).toBe(
65+
JSON.stringify(mockData)
66+
);
67+
});
68+
69+
it('renders responsive container', () => {
70+
render(<ReportChart {...defaultProps} />);
71+
expect(screen.getByTestId('responsive-container')).toBeInTheDocument();
72+
});
73+
});
74+
75+
describe('Chart Elements', () => {
76+
it('displays completed bar with correct color', () => {
77+
render(<ReportChart {...defaultProps} />);
78+
const completedBar = screen.getByTestId('bar-completed');
79+
expect(completedBar).toHaveAttribute('data-fill', '#E776CB');
80+
expect(completedBar).toHaveAttribute('data-name', 'Completed');
81+
});
82+
83+
it('displays ongoing bar with correct color', () => {
84+
render(<ReportChart {...defaultProps} />);
85+
const ongoingBar = screen.getByTestId('bar-ongoing');
86+
expect(ongoingBar).toHaveAttribute('data-fill', '#5FD9FA');
87+
expect(ongoingBar).toHaveAttribute('data-name', 'Ongoing');
88+
});
89+
90+
it('renders chart axes', () => {
91+
render(<ReportChart {...defaultProps} />);
92+
expect(screen.getByTestId('x-axis')).toBeInTheDocument();
93+
expect(screen.getByTestId('y-axis')).toBeInTheDocument();
94+
});
95+
96+
it('renders legend', () => {
97+
render(<ReportChart {...defaultProps} />);
98+
expect(screen.getByTestId('legend')).toBeInTheDocument();
99+
});
100+
101+
it('renders tooltip', () => {
102+
render(<ReportChart {...defaultProps} />);
103+
expect(screen.getByTestId('tooltip')).toBeInTheDocument();
104+
});
105+
106+
it('renders cartesian grid', () => {
107+
render(<ReportChart {...defaultProps} />);
108+
expect(screen.getByTestId('cartesian-grid')).toBeInTheDocument();
109+
});
110+
});
111+
112+
describe('CSV Export', () => {
113+
it('triggers CSV export when button clicked', () => {
114+
render(<ReportChart {...defaultProps} />);
115+
const csvButton = screen.getByTitle('Download as CSV');
116+
117+
fireEvent.click(csvButton);
118+
119+
expect(downloadUtils.exportReportToCSV).toHaveBeenCalledWith(
120+
mockData,
121+
'Daily Report'
122+
);
123+
});
124+
125+
it('does not disable CSV button during PNG export', async () => {
126+
(downloadUtils.exportChartToPNG as jest.Mock).mockImplementation(
127+
() => new Promise((resolve) => setTimeout(resolve, 100))
128+
);
129+
130+
render(<ReportChart {...defaultProps} />);
131+
const pngButton = screen.getByTitle('Download as PNG');
132+
const csvButton = screen.getByTitle('Download as CSV');
133+
134+
fireEvent.click(pngButton);
135+
136+
expect(csvButton).toBeDisabled();
137+
});
138+
});
139+
140+
describe('PNG Export', () => {
141+
it('triggers PNG export when button clicked', async () => {
142+
render(<ReportChart {...defaultProps} />);
143+
const pngButton = screen.getByTitle('Download as PNG');
144+
145+
fireEvent.click(pngButton);
146+
147+
await waitFor(() => {
148+
expect(downloadUtils.exportChartToPNG).toHaveBeenCalledWith(
149+
'daily-report-chart',
150+
'Daily Report'
151+
);
152+
});
153+
});
154+
155+
it('shows loading spinner during export', async () => {
156+
(downloadUtils.exportChartToPNG as jest.Mock).mockImplementation(
157+
() => new Promise((resolve) => setTimeout(resolve, 100))
158+
);
159+
160+
render(<ReportChart {...defaultProps} />);
161+
const pngButton = screen.getByTitle('Download as PNG');
162+
163+
fireEvent.click(pngButton);
164+
165+
expect(pngButton).toBeDisabled();
166+
167+
await waitFor(() => {
168+
expect(downloadUtils.exportChartToPNG).toHaveBeenCalled();
169+
});
170+
});
171+
172+
it('re-enables button after export completes', async () => {
173+
render(<ReportChart {...defaultProps} />);
174+
const pngButton = screen.getByTitle('Download as PNG');
175+
176+
fireEvent.click(pngButton);
177+
178+
await waitFor(() => {
179+
expect(pngButton).not.toBeDisabled();
180+
});
181+
});
182+
});
183+
184+
describe('Multiple Data Points', () => {
185+
it('handles multiple data entries', () => {
186+
const multiData = [
187+
{ name: 'Week 1', completed: 10, ongoing: 5 },
188+
{ name: 'Week 2', completed: 8, ongoing: 7 },
189+
{ name: 'Week 3', completed: 12, ongoing: 3 },
190+
];
191+
192+
render(<ReportChart {...defaultProps} data={multiData} />);
193+
194+
const barChart = screen.getByTestId('bar-chart');
195+
expect(barChart.getAttribute('data-chart-data')).toBe(
196+
JSON.stringify(multiData)
197+
);
198+
});
199+
200+
it('handles zero values', () => {
201+
const zeroData = [{ name: 'Today', completed: 0, ongoing: 0 }];
202+
203+
render(<ReportChart {...defaultProps} data={zeroData} />);
204+
205+
const barChart = screen.getByTestId('bar-chart');
206+
expect(barChart).toBeInTheDocument();
207+
});
208+
});
209+
});

0 commit comments

Comments
 (0)