// File: frontend/components/common/__tests__/error-display.test.tsx
// Change Log:
// - 2026-06-13: Initial creation - test coverage for ErrorDisplay component and parseApiError helper
// - 2026-06-13: Refactor to remove blank lines inside functions to satisfy project guidelines
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { ErrorDisplay, parseApiError } from '../error-display';
describe('ErrorDisplay Component', () => {
beforeEach(() => {
vi.stubGlobal('window', {
open: vi.fn(),
});
});
it('ควรส่งกลับ null เมื่อไม่มี error หรือ payload', () => {
const { container } = render();
expect(container.firstChild).toBeNull();
});
it('ควรเรนเดอร์ในโหมด compact สำเร็จ', () => {
const errorPayload = {
type: 'VALIDATION_ERROR',
code: 'ERR_VAL',
message: 'Validation failed',
severity: 'LOW' as const,
timestamp: new Date().toISOString(),
};
render();
expect(screen.getByText('Validation failed')).toBeInTheDocument();
});
it('ควรเรนเดอร์ในโหมดปกติพร้อม Recovery Actions สำเร็จ', () => {
const errorResponse = {
error: {
type: 'SYSTEM_ERROR',
code: 'ERR_SYS',
message: 'System crashed',
severity: 'CRITICAL' as const,
timestamp: new Date().toISOString(),
recoveryActions: ['Restart app', 'Clear cache'],
},
};
render();
expect(screen.getByText('System crashed')).toBeInTheDocument();
expect(screen.getByText('วิธีแก้ไข:')).toBeInTheDocument();
expect(screen.getByText('Restart app')).toBeInTheDocument();
expect(screen.getByText('Clear cache')).toBeInTheDocument();
});
it('ควรเรนเดอร์รายละเอียดทางเทคนิคเมื่อรันในสภาพแวดล้อม development', () => {
const originalEnv = process.env.NODE_ENV;
process.env.NODE_ENV = 'development';
const errorPayload = {
type: 'DATABASE_ERROR',
code: 'ERR_DB',
message: 'DB connection lost',
severity: 'HIGH' as const,
timestamp: new Date().toISOString(),
technicalMessage: 'Failed to connect to host postgres://localhost:5432',
};
render();
expect(screen.getByText('รายละเอียดทางเทคนิค (Development)')).toBeInTheDocument();
expect(screen.getByText('Failed to connect to host postgres://localhost:5432')).toBeInTheDocument();
process.env.NODE_ENV = originalEnv;
});
it('ควรเรียก onRetry เมื่อคลิกปุ่มลองใหม่', () => {
const mockOnRetry = vi.fn();
const errorPayload = {
type: 'API_ERROR',
code: 'ERR_API',
message: 'API failed',
severity: 'MEDIUM' as const,
timestamp: new Date().toISOString(),
};
render();
const retryBtn = screen.getByText('ลองใหม่');
fireEvent.click(retryBtn);
expect(mockOnRetry).toHaveBeenCalled();
});
it('ควรเปิดเมลเมื่อคลิกปุ่มติดต่อผู้ดูแลระบบ', () => {
const errorPayload = {
type: 'API_ERROR',
code: 'ERR_API',
message: 'API failed',
severity: 'MEDIUM' as const,
timestamp: new Date().toISOString(),
};
render();
const supportBtn = screen.getByText('ติดต่อผู้ดูแลระบบ');
fireEvent.click(supportBtn);
expect(window.open).toHaveBeenCalledWith('mailto:support@np-dms.work', '_blank');
});
});
describe('parseApiError helper', () => {
it('ควรจัดการข้อผิดพลาดจากโครงสร้าง Axios Error ได้อย่างถูกต้อง', () => {
const mockAxiosError = {
response: {
data: {
error: {
type: 'API_ERROR',
code: 'ERR_CODE',
message: 'Mock Axios Error',
severity: 'MEDIUM' as const,
timestamp: '2026-06-13T00:00:00.000Z',
},
},
},
};
const parsed = parseApiError(mockAxiosError);
expect(parsed.error.message).toBe('Mock Axios Error');
expect(parsed.error.code).toBe('ERR_CODE');
});
it('ควรคืนค่าเดิมถ้าเป็นโครงสร้าง ApiErrorResponse อยู่แล้ว', () => {
const mockResponse = {
error: {
type: 'CUSTOM_ERROR',
code: 'ERR_CUSTOM',
message: 'Mock Custom Error',
severity: 'LOW' as const,
timestamp: '2026-06-13T00:00:00.000Z',
},
};
const parsed = parseApiError(mockResponse);
expect(parsed).toEqual(mockResponse);
});
it('ควรส่งกลับ Internal/Network error เมื่อมีข้อผิดพลาดที่ไม่รู้จัก', () => {
const parsed = parseApiError('Random Error String');
expect(parsed.error.type).toBe('INTERNAL_ERROR');
expect(parsed.error.code).toBe('NETWORK_ERROR');
expect(parsed.error.message).toBe('ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้');
});
});