Files
lcbp3/frontend/components/circulation/__tests__/circulation-list.test.tsx
T
admin 7e8f4859cd
CI / CD Pipeline / build (push) Failing after 6m24s
CI / CD Pipeline / deploy (push) Has been skipped
feat(ai): add ADR-036 unified OCR architecture and frontend test coverage
- Add ADR-036 unified OCR architecture (typhoon-ocr via Ollama)
- Extend AI execution profiles for OCR sandbox configuration
- Add comprehensive frontend test coverage (components, hooks, services)
- Add backend test coverage for document-numbering services
- Update OCR sidecar with typhoon-ocr integration
- Add AI policy service and execution profile management
- Update AGENTS.md and architecture documentation
2026-06-14 06:34:07 +07:00

198 lines
7.4 KiB
TypeScript

// File: frontend/components/circulation/__tests__/circulation-list.test.tsx
// Change Log:
// - 2026-06-13: Initial creation - test coverage for CirculationList component
// - 2026-06-14: Render column cells in DataTable mock to cover list formatting logic
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { CirculationList } from '../circulation-list';
import { Circulation, CirculationListResponse } from '@/types/circulation';
import { ColumnDef } from '@tanstack/react-table';
import React from 'react';
vi.mock('@/components/common/data-table', () => ({
DataTable: ({ data, columns }: { data: Circulation[]; columns: ColumnDef<Circulation>[] }) => {
type MockCellContext = {
row: {
original: Circulation;
getValue: (key: string) => unknown;
};
};
const renderCell = (column: ColumnDef<Circulation>, row: Circulation): React.ReactNode => {
if (!column.cell || typeof column.cell !== 'function') return null;
const key = 'accessorKey' in column ? String(column.accessorKey) : '';
const context: MockCellContext = {
row: {
original: row,
getValue: (valueKey: string) => row[valueKey as keyof Circulation],
},
};
return (
<div data-testid={`cell-${key || column.id || 'custom'}-${row.publicId}`}>
{column.cell(context as never)}
</div>
);
};
return (
<div data-testid="data-table">
<span data-testid="row-count">{data.length} rows</span>
<span data-testid="col-count">{columns.length} columns</span>
{data.map((row) => (
<div key={row.publicId} data-testid={`row-${row.publicId}`}>
{columns.map((column) => (
<React.Fragment key={String(('accessorKey' in column && column.accessorKey) || column.id)}>
{renderCell(column, row)}
</React.Fragment>
))}
</div>
))}
</div>
);
},
}));
vi.mock('next/link', () => ({
default: ({ children, href }: { children: React.ReactNode; href: string }) => (
<a href={href}>{children}</a>
),
}));
const createRouting = (status: 'PENDING' | 'COMPLETED'): Circulation['routings'][number] => ({
id: status === 'COMPLETED' ? 1 : 2,
circulationId: 1,
stepNumber: status === 'COMPLETED' ? 1 : 2,
organizationId: 1,
status,
createdAt: '2026-06-01T00:00:00Z',
updatedAt: '2026-06-01T00:00:00Z',
});
// Mock CirculationListResponse data ตาม ADR-019 (UUIDv7)
const mockResponse: CirculationListResponse = {
data: [
{
publicId: '019505a1-7c3e-7000-8000-abc123def001',
organizationId: 1,
circulationNo: 'CIR-2026-001',
subject: 'Test Circulation',
statusCode: 'ACTIVE',
createdByUserId: 1,
organization: {
publicId: '019505a1-7c3e-7000-8000-abc123def010',
organizationName: 'Test Org',
organizationCode: 'ORG',
},
routings: [createRouting('COMPLETED'), createRouting('PENDING')],
createdAt: '2026-06-01T00:00:00Z',
updatedAt: '2026-06-01T00:00:00Z',
},
],
meta: { total: 1, page: 1, limit: 20, totalPages: 1 },
};
describe('CirculationList', () => {
it('ควรเรนเดอร์ DataTable ได้ถูกต้อง', () => {
render(<CirculationList data={mockResponse} />);
expect(screen.getByTestId('data-table')).toBeInTheDocument();
});
it('ควรแสดงจำนวน rows ถูกต้อง', () => {
render(<CirculationList data={mockResponse} />);
expect(screen.getByTestId('row-count')).toHaveTextContent('1 rows');
});
it('ควรแสดงข้อมูล column cells หลักได้ถูกต้อง', () => {
render(<CirculationList data={mockResponse} />);
expect(screen.getByText('CIR-2026-001')).toBeInTheDocument();
expect(screen.getByText('Test Circulation')).toBeInTheDocument();
expect(screen.getByText('Test Org')).toBeInTheDocument();
expect(screen.getByText('ACTIVE')).toBeInTheDocument();
expect(screen.getByText('1/2')).toBeInTheDocument();
expect(screen.getByText('01 Jun 2026')).toBeInTheDocument();
expect(screen.getByTitle('View Details').closest('a')).toHaveAttribute(
'href',
'/circulation/019505a1-7c3e-7000-8000-abc123def001'
);
});
it('ควรแสดง fallback เมื่อไม่มี organization และ routings', () => {
const response: CirculationListResponse = {
data: [
{
publicId: '019505a1-7c3e-7000-8000-abc123def002',
organizationId: 1,
circulationNo: 'CIR-2026-002',
subject: 'No Routing',
statusCode: 'DRAFT',
createdByUserId: 1,
createdAt: '2026-06-02T00:00:00Z',
updatedAt: '2026-06-02T00:00:00Z',
},
],
meta: { total: 1, page: 1, limit: 20, totalPages: 1 },
};
render(<CirculationList data={response} />);
expect(screen.getByText('DRAFT')).toBeInTheDocument();
expect(screen.getAllByText('-')).toHaveLength(2);
});
it('ควร map status variant ของสถานะ completed และ unknown โดยไม่ error', () => {
const response: CirculationListResponse = {
data: [
{
publicId: '019505a1-7c3e-7000-8000-abc123def003',
organizationId: 1,
circulationNo: 'CIR-2026-003',
subject: 'Completed Routing',
statusCode: 'COMPLETED',
createdByUserId: 1,
routings: [createRouting('COMPLETED')],
createdAt: '2026-06-03T00:00:00Z',
updatedAt: '2026-06-03T00:00:00Z',
},
{
publicId: '019505a1-7c3e-7000-8000-abc123def004',
organizationId: 1,
circulationNo: 'CIR-2026-004',
subject: 'Unknown Status',
statusCode: 'ARCHIVED',
createdByUserId: 1,
createdAt: '2026-06-04T00:00:00Z',
updatedAt: '2026-06-04T00:00:00Z',
},
],
meta: { total: 2, page: 1, limit: 20, totalPages: 1 },
};
render(<CirculationList data={response} />);
expect(screen.getByText('COMPLETED')).toBeInTheDocument();
expect(screen.getByText('ARCHIVED')).toBeInTheDocument();
expect(screen.getByText('1/1')).toBeInTheDocument();
});
it('ควรแสดง meta total ที่ด้านล่างเมื่อมี meta', () => {
render(<CirculationList data={mockResponse} />);
expect(screen.getByText(/Showing 1 of 1 circulations/)).toBeInTheDocument();
});
it('ควรไม่แสดง meta เมื่อไม่มี meta', () => {
const noMeta = { data: [] } as CirculationListResponse;
render(<CirculationList data={noMeta} />);
expect(screen.queryByText(/Showing/)).not.toBeInTheDocument();
});
it('ควร return null เมื่อ data เป็น null/undefined', () => {
const { container } = render(<CirculationList data={null as unknown as CirculationListResponse} />);
expect(container).toBeEmptyDOMElement();
});
it('ควรเรนเดอร์ empty state เมื่อ data.data เป็น array ว่าง', () => {
const emptyResponse: CirculationListResponse = {
data: [],
meta: { total: 0, page: 1, limit: 20, totalPages: 0 },
};
render(<CirculationList data={emptyResponse} />);
expect(screen.getByTestId('row-count')).toHaveTextContent('0 rows');
expect(screen.getByText(/Showing 0 of 0 circulations/)).toBeInTheDocument();
});
});