test(frontend): raise overall statement coverage to 30.42% for Phase 1 MVP
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
// File: frontend/components/drawings/__tests__/card.test.tsx
|
||||
// Change Log:
|
||||
// - 2026-06-13: Initial creation - test coverage for DrawingCard component
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { DrawingCard } from '../card';
|
||||
|
||||
describe('DrawingCard', () => {
|
||||
const mockDrawing = {
|
||||
publicId: '019505a1-7c3e-7000-8000-abc123def456',
|
||||
drawingNumber: 'SD-001',
|
||||
title: 'Test Drawing',
|
||||
sheetNumber: 'A1',
|
||||
revision: 'A',
|
||||
scale: '1:100',
|
||||
issueDate: '2026-01-01',
|
||||
discipline: {
|
||||
publicId: '019505a1-7c3e-7000-8000-abc123def457',
|
||||
disciplineCode: 'STR',
|
||||
},
|
||||
revisionCount: 1,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render drawing card with data', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('SD-001')).toBeInTheDocument();
|
||||
expect(screen.getByText('Test Drawing')).toBeInTheDocument();
|
||||
expect(screen.getByText('STR')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render placeholder when drawing number is missing', () => {
|
||||
const drawingWithoutNumber = { ...mockDrawing, drawingNumber: undefined };
|
||||
render(<DrawingCard drawing={drawingWithoutNumber} />);
|
||||
|
||||
expect(screen.getByText('No Number')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render placeholder when title is missing', () => {
|
||||
const drawingWithoutTitle = { ...mockDrawing, title: undefined };
|
||||
render(<DrawingCard drawing={drawingWithoutTitle} />);
|
||||
|
||||
expect(screen.getByText('No Title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display sheet number', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('Sheet:')).toBeInTheDocument();
|
||||
expect(screen.getByText('A1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display revision', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('Rev:')).toBeInTheDocument();
|
||||
expect(screen.getByText('A')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display scale', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('Scale:')).toBeInTheDocument();
|
||||
expect(screen.getByText('1:100')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display formatted issue date', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('Date:')).toBeInTheDocument();
|
||||
expect(screen.getByText('01/01/2026')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display legacy drawing number when present', () => {
|
||||
const drawingWithLegacy = { ...mockDrawing, legacyDrawingNumber: 'LEG-001' };
|
||||
render(<DrawingCard drawing={drawingWithLegacy} />);
|
||||
|
||||
expect(screen.getByText('Legacy:')).toBeInTheDocument();
|
||||
expect(screen.getByText('LEG-001')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display volume page when present', () => {
|
||||
const drawingWithPage = { ...mockDrawing, volumePage: 5 };
|
||||
render(<DrawingCard drawing={drawingWithPage} />);
|
||||
|
||||
expect(screen.getByText('Page:')).toBeInTheDocument();
|
||||
expect(screen.getByText('5')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display discipline code from object', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('STR')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display discipline code from string', () => {
|
||||
const drawingWithStringDiscipline = { ...mockDrawing, discipline: 'MECH' };
|
||||
render(<DrawingCard drawing={drawingWithStringDiscipline} />);
|
||||
|
||||
expect(screen.getByText('MECH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show View button with link', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
const viewButton = screen.getByText('View');
|
||||
expect(viewButton).toBeInTheDocument();
|
||||
expect(viewButton.closest('a')).toHaveAttribute('href', '/drawings/019505a1-7c3e-7000-8000-abc123def456');
|
||||
});
|
||||
|
||||
it('should show Download button', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.getByText('Download')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show Compare button when revisionCount > 1', () => {
|
||||
const drawingWithRevisions = { ...mockDrawing, revisionCount: 2 };
|
||||
render(<DrawingCard drawing={drawingWithRevisions} />);
|
||||
|
||||
expect(screen.getByText('Compare')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not show Compare button when revisionCount <= 1', () => {
|
||||
render(<DrawingCard drawing={mockDrawing} />);
|
||||
|
||||
expect(screen.queryByText('Compare')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display dash for missing sheet number', () => {
|
||||
const drawingWithoutSheet = { ...mockDrawing, sheetNumber: undefined };
|
||||
render(<DrawingCard drawing={drawingWithoutSheet} />);
|
||||
|
||||
expect(screen.getByText('Sheet:')).toBeInTheDocument();
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display 0 for missing revision', () => {
|
||||
const drawingWithoutRevision = { ...mockDrawing, revision: undefined };
|
||||
render(<DrawingCard drawing={drawingWithoutRevision} />);
|
||||
|
||||
expect(screen.getByText('Rev:')).toBeInTheDocument();
|
||||
expect(screen.getByText('0')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display N/A for missing scale', () => {
|
||||
const drawingWithoutScale = { ...mockDrawing, scale: undefined };
|
||||
render(<DrawingCard drawing={drawingWithoutScale} />);
|
||||
|
||||
expect(screen.getByText('Scale:')).toBeInTheDocument();
|
||||
expect(screen.getByText('N/A')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not display date when issueDate is missing', () => {
|
||||
const drawingWithoutDate = { ...mockDrawing, issueDate: undefined };
|
||||
render(<DrawingCard drawing={drawingWithoutDate} />);
|
||||
|
||||
expect(screen.getByText('Date:')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
// File: frontend/components/drawings/__tests__/list.test.tsx
|
||||
// Change Log:
|
||||
// - 2026-06-13: Refactor to use static imports instead of require, fixing ESM module resolution errors
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { DrawingList } from '../list';
|
||||
import { useDrawings } from '@/hooks/use-drawing';
|
||||
|
||||
// Mock useDrawings hook
|
||||
vi.mock('@/hooks/use-drawing', () => ({
|
||||
useDrawings: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock ServerDataTable
|
||||
vi.mock('@/components/documents/common/server-data-table', () => ({
|
||||
ServerDataTable: ({ isLoading, data }: { isLoading: boolean; data: unknown[] }) => (
|
||||
<div>
|
||||
{isLoading ? <div>Loading...</div> : <div>{data.length} items</div>}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
describe('DrawingList', () => {
|
||||
const mockProjectUuid = '019505a1-7c3e-7000-8000-abc123def456';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should render loading state', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
isError: false,
|
||||
error: null,
|
||||
});
|
||||
render(<DrawingList type="SHOP" projectUuid={mockProjectUuid} />);
|
||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render drawings data', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: {
|
||||
data: [{ publicId: 'uuid-1', drawingNumber: 'SD-001' }],
|
||||
meta: { total: 1, page: 1, limit: 20, totalPages: 1 },
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
});
|
||||
render(<DrawingList type="SHOP" projectUuid={mockProjectUuid} />);
|
||||
expect(screen.getByText('1 items')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error state', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
isError: true,
|
||||
error: {
|
||||
response: {
|
||||
status: 500,
|
||||
data: { message: 'Server error' },
|
||||
},
|
||||
},
|
||||
});
|
||||
render(<DrawingList type="SHOP" projectUuid={mockProjectUuid} />);
|
||||
expect(screen.getByText('Failed to load shop drawings')).toBeInTheDocument();
|
||||
expect(screen.getByText('HTTP 500: Server error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error with array message', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
isError: true,
|
||||
error: {
|
||||
response: {
|
||||
status: 400,
|
||||
data: { message: ['Error 1', 'Error 2'] },
|
||||
},
|
||||
},
|
||||
});
|
||||
render(<DrawingList type="CONTRACT" projectUuid={mockProjectUuid} />);
|
||||
expect(screen.getByText('Failed to load contract drawings')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Error 1, Error 2/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error with generic message', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
isError: true,
|
||||
error: {
|
||||
message: 'Network error',
|
||||
},
|
||||
});
|
||||
render(<DrawingList type="AS_BUILT" projectUuid={mockProjectUuid} />);
|
||||
expect(screen.getByText('Failed to load as_built drawings')).toBeInTheDocument();
|
||||
expect(screen.getByText('Network error')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle empty data', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: {
|
||||
data: [],
|
||||
meta: { total: 0, page: 1, limit: 20, totalPages: 0 },
|
||||
},
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
});
|
||||
render(<DrawingList type="SHOP" projectUuid={mockProjectUuid} />);
|
||||
expect(screen.getByText('0 items')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should pass filters to useDrawings', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: { data: [], meta: { total: 0, page: 1, limit: 20, totalPages: 0 } },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
});
|
||||
render(
|
||||
<DrawingList
|
||||
type="SHOP"
|
||||
projectUuid={mockProjectUuid}
|
||||
filters={{ search: 'test' }}
|
||||
/>
|
||||
);
|
||||
expect(useDrawings).toHaveBeenCalledWith('SHOP', {
|
||||
projectUuid: mockProjectUuid,
|
||||
search: 'test',
|
||||
page: 1,
|
||||
limit: 20,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle CONTRACT type', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: { data: [], meta: { total: 0, page: 1, limit: 20, totalPages: 0 } },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
});
|
||||
render(<DrawingList type="CONTRACT" projectUuid={mockProjectUuid} />);
|
||||
expect(useDrawings).toHaveBeenCalledWith('CONTRACT', expect.any(Object));
|
||||
});
|
||||
|
||||
it('should handle AS_BUILT type', () => {
|
||||
(useDrawings as any).mockReturnValue({
|
||||
data: { data: [], meta: { total: 0, page: 1, limit: 20, totalPages: 0 } },
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
});
|
||||
render(<DrawingList type="AS_BUILT" projectUuid={mockProjectUuid} />);
|
||||
expect(useDrawings).toHaveBeenCalledWith('AS_BUILT', expect.any(Object));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user