- Add comprehensive TDD documentation to CLAUDE.md with coverage requirements and examples - Extract reusable UI components to frontend/src/components/ui/ (Modal, FormInput, Button, Alert, etc.) - Add shared constants (schedulePresets) and utility hooks (useCrudMutation, useFormValidation) - Update frontend/CLAUDE.md with component documentation and usage examples - Refactor CreateTaskModal to use shared components and constants - Fix test assertions to be more robust and accurate across all test files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
896 lines
22 KiB
TypeScript
896 lines
22 KiB
TypeScript
/**
|
|
* Unit tests for ChartWidget component
|
|
*
|
|
* Tests cover:
|
|
* - Chart container rendering
|
|
* - Title display
|
|
* - Bar chart rendering
|
|
* - Line chart rendering
|
|
* - Data visualization
|
|
* - Custom colors
|
|
* - Value prefixes
|
|
* - Edit mode with drag handle and remove button
|
|
* - Tooltip formatting
|
|
* - Responsive container
|
|
* - Accessibility
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import React from 'react';
|
|
import ChartWidget from '../ChartWidget';
|
|
|
|
// Mock Recharts components to avoid rendering issues in tests
|
|
vi.mock('recharts', () => ({
|
|
ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
|
|
<div data-testid="responsive-container">{children}</div>
|
|
),
|
|
BarChart: ({ data, children }: { data: any[]; children: React.ReactNode }) => (
|
|
<div data-testid="bar-chart" data-chart-data={JSON.stringify(data)}>
|
|
{children}
|
|
</div>
|
|
),
|
|
LineChart: ({ data, children }: { data: any[]; children: React.ReactNode }) => (
|
|
<div data-testid="line-chart" data-chart-data={JSON.stringify(data)}>
|
|
{children}
|
|
</div>
|
|
),
|
|
Bar: ({ dataKey, fill }: { dataKey: string; fill: string }) => (
|
|
<div data-testid="bar" data-key={dataKey} data-fill={fill} />
|
|
),
|
|
Line: ({ dataKey, stroke }: { dataKey: string; stroke: string }) => (
|
|
<div data-testid="line" data-key={dataKey} data-stroke={stroke} />
|
|
),
|
|
XAxis: ({ dataKey }: { dataKey: string }) => (
|
|
<div data-testid="x-axis" data-key={dataKey} />
|
|
),
|
|
YAxis: () => <div data-testid="y-axis" />,
|
|
CartesianGrid: () => <div data-testid="cartesian-grid" />,
|
|
Tooltip: () => <div data-testid="tooltip" />,
|
|
}));
|
|
|
|
describe('ChartWidget', () => {
|
|
const mockChartData = [
|
|
{ name: 'Mon', value: 100 },
|
|
{ name: 'Tue', value: 150 },
|
|
{ name: 'Wed', value: 120 },
|
|
{ name: 'Thu', value: 180 },
|
|
{ name: 'Fri', value: 200 },
|
|
];
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('Rendering', () => {
|
|
it('should render the component', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue Chart"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Revenue Chart')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render chart container', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue Chart"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const container = screen.getByTestId('responsive-container');
|
|
expect(container).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render with different titles', () => {
|
|
const { rerender } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Revenue')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<ChartWidget
|
|
title="Appointments"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Appointments')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render with empty data array', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Empty Chart"
|
|
data={[]}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Empty Chart')).toBeInTheDocument();
|
|
expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Title', () => {
|
|
it('should display title with correct styling', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Weekly Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const title = screen.getByText('Weekly Revenue');
|
|
expect(title).toBeInTheDocument();
|
|
expect(title).toHaveClass('text-lg', 'font-semibold', 'text-gray-900');
|
|
});
|
|
|
|
it('should apply dark mode styles to title', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const title = screen.getByText('Revenue');
|
|
expect(title).toHaveClass('dark:text-white');
|
|
});
|
|
|
|
it('should handle long titles', () => {
|
|
const longTitle = 'Very Long Chart Title That Should Still Display Properly Without Breaking Layout';
|
|
render(
|
|
<ChartWidget
|
|
title={longTitle}
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText(longTitle)).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Bar Chart', () => {
|
|
it('should render bar chart when type is "bar"', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
|
|
expect(screen.queryByTestId('line-chart')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('should pass data to bar chart', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const barChart = screen.getByTestId('bar-chart');
|
|
const chartData = JSON.parse(barChart.getAttribute('data-chart-data') || '[]');
|
|
expect(chartData).toEqual(mockChartData);
|
|
});
|
|
|
|
it('should render bar with correct dataKey', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const bar = screen.getByTestId('bar');
|
|
expect(bar).toHaveAttribute('data-key', 'value');
|
|
});
|
|
|
|
it('should render bar with default color', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const bar = screen.getByTestId('bar');
|
|
expect(bar).toHaveAttribute('data-fill', '#3b82f6');
|
|
});
|
|
|
|
it('should render bar with custom color', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
color="#10b981"
|
|
/>
|
|
);
|
|
|
|
const bar = screen.getByTestId('bar');
|
|
expect(bar).toHaveAttribute('data-fill', '#10b981');
|
|
});
|
|
|
|
it('should render CartesianGrid for bar chart', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('cartesian-grid')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render XAxis with name dataKey', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const xAxis = screen.getByTestId('x-axis');
|
|
expect(xAxis).toHaveAttribute('data-key', 'name');
|
|
});
|
|
|
|
it('should render YAxis', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('y-axis')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should render Tooltip', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('tooltip')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Line Chart', () => {
|
|
it('should render line chart when type is "line"', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('line-chart')).toBeInTheDocument();
|
|
expect(screen.queryByTestId('bar-chart')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('should pass data to line chart', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
const lineChart = screen.getByTestId('line-chart');
|
|
const chartData = JSON.parse(lineChart.getAttribute('data-chart-data') || '[]');
|
|
expect(chartData).toEqual(mockChartData);
|
|
});
|
|
|
|
it('should render line with correct dataKey', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
const line = screen.getByTestId('line');
|
|
expect(line).toHaveAttribute('data-key', 'value');
|
|
});
|
|
|
|
it('should render line with default color', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
const line = screen.getByTestId('line');
|
|
expect(line).toHaveAttribute('data-stroke', '#3b82f6');
|
|
});
|
|
|
|
it('should render line with custom color', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
color="#ef4444"
|
|
/>
|
|
);
|
|
|
|
const line = screen.getByTestId('line');
|
|
expect(line).toHaveAttribute('data-stroke', '#ef4444');
|
|
});
|
|
|
|
it('should render CartesianGrid for line chart', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('cartesian-grid')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should switch between chart types', () => {
|
|
const { rerender } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('line-chart')).toBeInTheDocument();
|
|
expect(screen.queryByTestId('bar-chart')).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Value Prefix', () => {
|
|
it('should use empty prefix by default', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Appointments"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
// Component renders successfully without prefix
|
|
expect(screen.getByText('Appointments')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should accept custom value prefix', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
valuePrefix="$"
|
|
/>
|
|
);
|
|
|
|
// Component renders successfully with prefix
|
|
expect(screen.getByText('Revenue')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should accept different prefixes', () => {
|
|
const { rerender } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
valuePrefix="$"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Revenue')).toBeInTheDocument();
|
|
|
|
rerender(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
valuePrefix="€"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Revenue')).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Edit Mode', () => {
|
|
it('should not show edit controls when isEditing is false', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={false}
|
|
/>
|
|
);
|
|
|
|
const dragHandle = container.querySelector('.drag-handle');
|
|
expect(dragHandle).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('should show drag handle when in edit mode', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={true}
|
|
/>
|
|
);
|
|
|
|
const dragHandle = container.querySelector('.drag-handle');
|
|
expect(dragHandle).toBeInTheDocument();
|
|
});
|
|
|
|
it('should show remove button when in edit mode', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={true}
|
|
onRemove={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
const removeButton = screen.getByRole('button');
|
|
expect(removeButton).toBeInTheDocument();
|
|
});
|
|
|
|
it('should call onRemove when remove button is clicked', async () => {
|
|
const user = userEvent.setup();
|
|
const handleRemove = vi.fn();
|
|
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={true}
|
|
onRemove={handleRemove}
|
|
/>
|
|
);
|
|
|
|
const removeButton = screen.getByRole('button');
|
|
await user.click(removeButton);
|
|
|
|
expect(handleRemove).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should apply padding to title when in edit mode', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={true}
|
|
/>
|
|
);
|
|
|
|
const title = screen.getByText('Revenue');
|
|
expect(title).toHaveClass('pl-5');
|
|
});
|
|
|
|
it('should not apply padding to title when not in edit mode', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={false}
|
|
/>
|
|
);
|
|
|
|
const title = screen.getByText('Revenue');
|
|
expect(title).not.toHaveClass('pl-5');
|
|
});
|
|
|
|
it('should have grab cursor on drag handle', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={true}
|
|
/>
|
|
);
|
|
|
|
const dragHandle = container.querySelector('.drag-handle');
|
|
expect(dragHandle).toHaveClass('cursor-grab', 'active:cursor-grabbing');
|
|
});
|
|
});
|
|
|
|
describe('Responsive Container', () => {
|
|
it('should render ResponsiveContainer', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByTestId('responsive-container')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should wrap chart in responsive container', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const container = screen.getByTestId('responsive-container');
|
|
const barChart = screen.getByTestId('bar-chart');
|
|
|
|
expect(container).toContainElement(barChart);
|
|
});
|
|
|
|
it('should have flex layout for proper sizing', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const widget = container.firstChild;
|
|
expect(widget).toHaveClass('flex', 'flex-col');
|
|
});
|
|
});
|
|
|
|
describe('Styling', () => {
|
|
it('should apply container styles', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const widget = container.firstChild;
|
|
expect(widget).toHaveClass(
|
|
'h-full',
|
|
'p-4',
|
|
'bg-white',
|
|
'rounded-xl',
|
|
'border',
|
|
'border-gray-200',
|
|
'shadow-sm',
|
|
'relative',
|
|
'group'
|
|
);
|
|
});
|
|
|
|
it('should apply dark mode styles', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const widget = container.firstChild;
|
|
expect(widget).toHaveClass('dark:bg-gray-800', 'dark:border-gray-700');
|
|
});
|
|
|
|
it('should have proper spacing for title', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const title = screen.getByText('Revenue');
|
|
expect(title).toHaveClass('mb-4');
|
|
});
|
|
|
|
it('should use flex-1 for chart container', () => {
|
|
const { container } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const chartContainer = container.querySelector('.flex-1');
|
|
expect(chartContainer).toBeInTheDocument();
|
|
expect(chartContainer).toHaveClass('min-h-0');
|
|
});
|
|
});
|
|
|
|
describe('Data Handling', () => {
|
|
it('should handle single data point', () => {
|
|
const singlePoint = [{ name: 'Mon', value: 100 }];
|
|
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={singlePoint}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const barChart = screen.getByTestId('bar-chart');
|
|
const chartData = JSON.parse(barChart.getAttribute('data-chart-data') || '[]');
|
|
expect(chartData).toEqual(singlePoint);
|
|
});
|
|
|
|
it('should handle large datasets', () => {
|
|
const largeData = Array.from({ length: 100 }, (_, i) => ({
|
|
name: `Day ${i + 1}`,
|
|
value: Math.random() * 1000,
|
|
}));
|
|
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={largeData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
const lineChart = screen.getByTestId('line-chart');
|
|
const chartData = JSON.parse(lineChart.getAttribute('data-chart-data') || '[]');
|
|
expect(chartData).toHaveLength(100);
|
|
});
|
|
|
|
it('should handle zero values', () => {
|
|
const zeroData = [
|
|
{ name: 'Mon', value: 0 },
|
|
{ name: 'Tue', value: 0 },
|
|
];
|
|
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={zeroData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const barChart = screen.getByTestId('bar-chart');
|
|
const chartData = JSON.parse(barChart.getAttribute('data-chart-data') || '[]');
|
|
expect(chartData).toEqual(zeroData);
|
|
});
|
|
|
|
it('should handle negative values', () => {
|
|
const negativeData = [
|
|
{ name: 'Mon', value: -50 },
|
|
{ name: 'Tue', value: 100 },
|
|
{ name: 'Wed', value: -30 },
|
|
];
|
|
|
|
render(
|
|
<ChartWidget
|
|
title="Profit/Loss"
|
|
data={negativeData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
const lineChart = screen.getByTestId('line-chart');
|
|
const chartData = JSON.parse(lineChart.getAttribute('data-chart-data') || '[]');
|
|
expect(chartData).toEqual(negativeData);
|
|
});
|
|
});
|
|
|
|
describe('Accessibility', () => {
|
|
it('should have semantic heading for title', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue Chart"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const heading = screen.getByRole('heading', { level: 3 });
|
|
expect(heading).toHaveTextContent('Revenue Chart');
|
|
});
|
|
|
|
it('should be keyboard accessible in edit mode', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
isEditing={true}
|
|
onRemove={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
const button = screen.getByRole('button');
|
|
expect(button).toBeInTheDocument();
|
|
});
|
|
|
|
it('should have proper color contrast', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
const title = screen.getByText('Revenue');
|
|
expect(title).toHaveClass('text-gray-900');
|
|
});
|
|
});
|
|
|
|
describe('Integration', () => {
|
|
it('should render correctly with all props', () => {
|
|
const handleRemove = vi.fn();
|
|
|
|
render(
|
|
<ChartWidget
|
|
title="Weekly Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
color="#10b981"
|
|
valuePrefix="$"
|
|
isEditing={true}
|
|
onRemove={handleRemove}
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Weekly Revenue')).toBeInTheDocument();
|
|
expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
|
|
expect(screen.getByTestId('bar')).toHaveAttribute('data-fill', '#10b981');
|
|
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should work with minimal props', () => {
|
|
render(
|
|
<ChartWidget
|
|
title="Simple Chart"
|
|
data={mockChartData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Simple Chart')).toBeInTheDocument();
|
|
expect(screen.getByTestId('bar-chart')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should maintain layout with varying data lengths', () => {
|
|
const shortData = [{ name: 'A', value: 1 }];
|
|
const { rerender } = render(
|
|
<ChartWidget
|
|
title="Data"
|
|
data={shortData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Data')).toBeInTheDocument();
|
|
|
|
const longData = Array.from({ length: 50 }, (_, i) => ({
|
|
name: `Item ${i}`,
|
|
value: i * 10,
|
|
}));
|
|
|
|
rerender(
|
|
<ChartWidget
|
|
title="Data"
|
|
data={longData}
|
|
type="bar"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Data')).toBeInTheDocument();
|
|
});
|
|
|
|
it('should support different color schemes', () => {
|
|
const colors = ['#3b82f6', '#10b981', '#ef4444', '#f59e0b', '#8b5cf6'];
|
|
|
|
const { rerender } = render(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
color={colors[0]}
|
|
/>
|
|
);
|
|
|
|
colors.forEach((color) => {
|
|
rerender(
|
|
<ChartWidget
|
|
title="Revenue"
|
|
data={mockChartData}
|
|
type="bar"
|
|
color={color}
|
|
/>
|
|
);
|
|
|
|
const bar = screen.getByTestId('bar');
|
|
expect(bar).toHaveAttribute('data-fill', color);
|
|
});
|
|
});
|
|
|
|
it('should handle rapid data updates', () => {
|
|
const { rerender } = render(
|
|
<ChartWidget
|
|
title="Live Data"
|
|
data={mockChartData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
const newData = mockChartData.map((item) => ({
|
|
...item,
|
|
value: item.value + Math.random() * 50,
|
|
}));
|
|
|
|
rerender(
|
|
<ChartWidget
|
|
title="Live Data"
|
|
data={newData}
|
|
type="line"
|
|
/>
|
|
);
|
|
|
|
expect(screen.getByText('Live Data')).toBeInTheDocument();
|
|
}
|
|
});
|
|
});
|
|
});
|