- Create BeneficiaryDetailPage with Overview, Sensors, and Activity tabs - Add StatusBadge and SensorStatusBadge UI components - Add Tabs component (Tabs, TabsList, TabsTrigger, TabsContent) - Add getBeneficiary API method - Include comprehensive tests for all new components - Update ESLint config with root:true to prevent config inheritance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
130 lines
3.4 KiB
TypeScript
130 lines
3.4 KiB
TypeScript
import React from 'react';
|
|
import { render, screen, waitFor, act } from '@testing-library/react';
|
|
import { useParams, useRouter } from 'next/navigation';
|
|
import BeneficiaryDetailPage from '../[id]/page';
|
|
|
|
// Mock next/navigation
|
|
jest.mock('next/navigation', () => ({
|
|
useParams: jest.fn(),
|
|
useRouter: jest.fn(),
|
|
}));
|
|
|
|
// Mock AdminLayout
|
|
jest.mock('../../../components/AdminLayout', () => {
|
|
return function MockAdminLayout({ children }: { children: React.ReactNode }) {
|
|
return <div data-testid="admin-layout">{children}</div>;
|
|
};
|
|
});
|
|
|
|
// Mock API calls
|
|
jest.mock('../../../lib/api', () => ({
|
|
getBeneficiary: jest.fn(),
|
|
getDevices: jest.fn(),
|
|
}));
|
|
|
|
import { getBeneficiary, getDevices } from '../../../lib/api';
|
|
|
|
const mockBeneficiary = {
|
|
id: 1,
|
|
first_name: 'John',
|
|
last_name: 'Doe',
|
|
email: 'john@example.com',
|
|
phone: '+1234567890',
|
|
address_street: '123 Main St',
|
|
address_city: 'New York',
|
|
address_country: 'USA',
|
|
created_at: '2024-01-15T10:00:00Z',
|
|
updated_at: '2024-01-20T10:00:00Z',
|
|
subscription_status: 'active',
|
|
subscription_plan: 'Premium',
|
|
};
|
|
|
|
const mockDevices = [
|
|
{
|
|
id: 1,
|
|
name: 'Living Room Sensor',
|
|
mac_address: 'AA:BB:CC:DD:EE:FF',
|
|
device_type: 'motion',
|
|
status: 'online',
|
|
last_seen: '2024-01-20T09:00:00Z',
|
|
firmware_version: '1.2.3',
|
|
},
|
|
];
|
|
|
|
describe('BeneficiaryDetailPage', () => {
|
|
const mockPush = jest.fn();
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
(useParams as jest.Mock).mockReturnValue({ id: '1' });
|
|
(useRouter as jest.Mock).mockReturnValue({ push: mockPush });
|
|
(getBeneficiary as jest.Mock).mockResolvedValue({ beneficiary: mockBeneficiary });
|
|
(getDevices as jest.Mock).mockResolvedValue({ devices: mockDevices });
|
|
});
|
|
|
|
it('renders beneficiary information after loading', async () => {
|
|
await act(async () => {
|
|
render(<BeneficiaryDetailPage />);
|
|
});
|
|
|
|
// Wait for loading to complete - check for Overview tab which appears after load
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText('Overview')).toBeInTheDocument();
|
|
},
|
|
{ timeout: 3000 }
|
|
);
|
|
|
|
// Verify beneficiary data is rendered (may appear multiple times in different sections)
|
|
const nameElements = screen.getAllByText('John Doe');
|
|
expect(nameElements.length).toBeGreaterThanOrEqual(1);
|
|
|
|
const emailElements = screen.getAllByText('john@example.com');
|
|
expect(emailElements.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it('renders tabs structure', async () => {
|
|
await act(async () => {
|
|
render(<BeneficiaryDetailPage />);
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText('Overview')).toBeInTheDocument();
|
|
},
|
|
{ timeout: 3000 }
|
|
);
|
|
|
|
expect(screen.getByText(/Sensors/)).toBeInTheDocument();
|
|
expect(screen.getByText('Activity History')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders error state when API fails', async () => {
|
|
(getBeneficiary as jest.Mock).mockRejectedValue(new Error('API Error'));
|
|
|
|
await act(async () => {
|
|
render(<BeneficiaryDetailPage />);
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText('API Error')).toBeInTheDocument();
|
|
},
|
|
{ timeout: 3000 }
|
|
);
|
|
});
|
|
|
|
it('renders back button navigation', async () => {
|
|
await act(async () => {
|
|
render(<BeneficiaryDetailPage />);
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText('Back to Beneficiaries')).toBeInTheDocument();
|
|
},
|
|
{ timeout: 3000 }
|
|
);
|
|
});
|
|
});
|