WellNuo/__tests__/utils/errorMessages.test.ts
Sergei a238b7e35f Add comprehensive error handling system
- Add extended error types with severity levels, retry policies,
  and contextual information (types/errors.ts)
- Create centralized error handler service with user-friendly
  message translation and classification (services/errorHandler.ts)
- Add ErrorContext for global error state management with
  auto-dismiss and error queue support (contexts/ErrorContext.tsx)
- Create error UI components: ErrorToast, FieldError,
  FieldErrorSummary, FullScreenError, EmptyState, OfflineState
- Add useError hook with retry strategies and API response handling
- Add useAsync hook for async operations with comprehensive state
- Create error message utilities with validation helpers
- Add tests for errorHandler and errorMessages (88 tests)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 17:43:07 -08:00

227 lines
7.7 KiB
TypeScript

/**
* Tests for Error Message Utilities
*/
import {
getErrorMessage,
getActionHint,
validateEmail,
validatePhone,
validateRequired,
validateMinLength,
validateMaxLength,
validateOtp,
formatErrorMessage,
combineFieldErrors,
getErrorCountText,
ErrorMessages,
} from '@/utils/errorMessages';
import { ErrorCodes } from '@/types/errors';
describe('errorMessages', () => {
describe('getErrorMessage', () => {
it('should return network error message', () => {
const message = getErrorMessage(ErrorCodes.NETWORK_ERROR);
expect(message).toBe(ErrorMessages.networkError);
});
it('should return session expired message for unauthorized', () => {
const message = getErrorMessage(ErrorCodes.UNAUTHORIZED);
expect(message).toBe(ErrorMessages.sessionExpired);
});
it('should return fallback for unknown code', () => {
const message = getErrorMessage('UNKNOWN_CODE', 'Custom fallback');
expect(message).toBe('Custom fallback');
});
it('should return generic error for unknown code without fallback', () => {
const message = getErrorMessage('UNKNOWN_CODE');
expect(message).toBe(ErrorMessages.genericError);
});
});
describe('getActionHint', () => {
it('should return hint for network error', () => {
const hint = getActionHint(ErrorCodes.NETWORK_ERROR);
expect(hint).toContain('connection');
});
it('should return hint for BLE error', () => {
const hint = getActionHint(ErrorCodes.BLE_NOT_ENABLED);
expect(hint).toContain('Bluetooth');
});
it('should return undefined for code without hint', () => {
const hint = getActionHint('UNKNOWN_CODE');
expect(hint).toBeUndefined();
});
});
describe('validateEmail', () => {
it('should return null for valid email', () => {
expect(validateEmail('test@example.com')).toBeNull();
expect(validateEmail('user.name@domain.co.uk')).toBeNull();
expect(validateEmail('user+tag@example.org')).toBeNull();
});
it('should return error for empty email', () => {
expect(validateEmail('')).toBe(ErrorMessages.required);
expect(validateEmail(' ')).toBe(ErrorMessages.required);
});
it('should return error for invalid email', () => {
expect(validateEmail('notanemail')).toBe(ErrorMessages.invalidEmail);
expect(validateEmail('missing@domain')).toBe(ErrorMessages.invalidEmail);
expect(validateEmail('@nodomain.com')).toBe(ErrorMessages.invalidEmail);
expect(validateEmail('spaces in@email.com')).toBe(ErrorMessages.invalidEmail);
});
});
describe('validatePhone', () => {
it('should return null for valid phone numbers', () => {
expect(validatePhone('+14155551234')).toBeNull();
expect(validatePhone('14155551234')).toBeNull();
expect(validatePhone('+7 (999) 123-45-67')).toBeNull();
expect(validatePhone('999.123.4567')).toBeNull();
});
it('should return null for empty phone (optional)', () => {
expect(validatePhone('')).toBeNull();
expect(validatePhone(' ')).toBeNull();
});
it('should return error for invalid phone', () => {
expect(validatePhone('123')).toBe(ErrorMessages.invalidPhone);
expect(validatePhone('abcdefghij')).toBe(ErrorMessages.invalidPhone);
});
});
describe('validateRequired', () => {
it('should return null for non-empty value', () => {
expect(validateRequired('test')).toBeNull();
expect(validateRequired(' test ')).toBeNull();
});
it('should return error for empty value', () => {
expect(validateRequired('')).toBe(ErrorMessages.required);
expect(validateRequired(' ')).toBe(ErrorMessages.required);
expect(validateRequired(null)).toBe(ErrorMessages.required);
expect(validateRequired(undefined)).toBe(ErrorMessages.required);
});
it('should include field name in error when provided', () => {
const error = validateRequired('', 'Email');
expect(error).toBe('Email is required.');
});
});
describe('validateMinLength', () => {
it('should return null when value meets minimum', () => {
expect(validateMinLength('abc', 3)).toBeNull();
expect(validateMinLength('abcde', 3)).toBeNull();
});
it('should return error when value is too short', () => {
expect(validateMinLength('ab', 3)).toBe(ErrorMessages.tooShort(3));
expect(validateMinLength('', 1)).toBe(ErrorMessages.tooShort(1));
});
});
describe('validateMaxLength', () => {
it('should return null when value meets maximum', () => {
expect(validateMaxLength('abc', 3)).toBeNull();
expect(validateMaxLength('ab', 3)).toBeNull();
});
it('should return error when value is too long', () => {
expect(validateMaxLength('abcd', 3)).toBe(ErrorMessages.tooLong(3));
});
});
describe('validateOtp', () => {
it('should return null for valid OTP', () => {
expect(validateOtp('123456')).toBeNull();
expect(validateOtp('000000')).toBeNull();
});
it('should return error for empty OTP', () => {
expect(validateOtp('')).toBe(ErrorMessages.required);
expect(validateOtp(' ')).toBe(ErrorMessages.required);
});
it('should return error for invalid OTP', () => {
expect(validateOtp('12345')).toBe(ErrorMessages.invalidOtp); // Too short
expect(validateOtp('1234567')).toBe(ErrorMessages.invalidOtp); // Too long
expect(validateOtp('12345a')).toBe(ErrorMessages.invalidOtp); // Contains letter
expect(validateOtp('12 345')).toBe(ErrorMessages.invalidOtp); // Contains space
});
it('should respect custom length', () => {
expect(validateOtp('1234', 4)).toBeNull();
expect(validateOtp('123456', 4)).toBe(ErrorMessages.invalidOtp);
});
});
describe('formatErrorMessage', () => {
it('should capitalize first letter', () => {
expect(formatErrorMessage('error message')).toBe('Error message.');
});
it('should add period if missing', () => {
expect(formatErrorMessage('Error message')).toBe('Error message.');
});
it('should not add period if already ends with punctuation', () => {
expect(formatErrorMessage('Error message.')).toBe('Error message.');
expect(formatErrorMessage('Error message!')).toBe('Error message!');
expect(formatErrorMessage('Error message?')).toBe('Error message?');
});
it('should return generic error for empty input', () => {
expect(formatErrorMessage('')).toBe(ErrorMessages.genericError);
});
it('should trim whitespace', () => {
expect(formatErrorMessage(' error ')).toBe('Error.');
});
});
describe('combineFieldErrors', () => {
it('should return empty string for no errors', () => {
expect(combineFieldErrors([])).toBe('');
});
it('should return single message for one error', () => {
const errors = [{ field: 'email', message: 'Invalid email' }];
expect(combineFieldErrors(errors)).toBe('Invalid email');
});
it('should combine multiple errors', () => {
const errors = [
{ field: 'email', message: 'Invalid email' },
{ field: 'phone', message: 'Invalid phone' },
];
const result = combineFieldErrors(errors);
expect(result).toContain('2 errors');
expect(result).toContain('Invalid email');
expect(result).toContain('Invalid phone');
});
});
describe('getErrorCountText', () => {
it('should return "No errors" for 0', () => {
expect(getErrorCountText(0)).toBe('No errors');
});
it('should return "1 error" for 1', () => {
expect(getErrorCountText(1)).toBe('1 error');
});
it('should return plural for multiple errors', () => {
expect(getErrorCountText(2)).toBe('2 errors');
expect(getErrorCountText(5)).toBe('5 errors');
});
});
});