UNPKG

@profullstack/auth-system

Version:

Flexible authentication system with user registration, login/logout, password reset, and session management

273 lines (227 loc) 11.4 kB
/** * Tests for validation utilities */ import { describe, it, expect, beforeEach } from 'vitest'; import { createValidationUtils } from '../../src/utils/validation.js'; describe('Validation Utils', () => { let validationUtils; beforeEach(() => { // Create validation utils validationUtils = createValidationUtils(); }); describe('isValidEmail', () => { it('should reject empty emails', () => { expect(validationUtils.isValidEmail('')).toBe(false); expect(validationUtils.isValidEmail(null)).toBe(false); expect(validationUtils.isValidEmail(undefined)).toBe(false); }); it('should reject invalid email formats', () => { expect(validationUtils.isValidEmail('not-an-email')).toBe(false); expect(validationUtils.isValidEmail('missing@tld')).toBe(false); expect(validationUtils.isValidEmail('@missing-local.com')).toBe(false); expect(validationUtils.isValidEmail('spaces in@email.com')).toBe(false); expect(validationUtils.isValidEmail('missing.domain@')).toBe(false); }); it('should accept valid email formats', () => { expect(validationUtils.isValidEmail('user@example.com')).toBe(true); expect(validationUtils.isValidEmail('user.name@example.com')).toBe(true); expect(validationUtils.isValidEmail('user+tag@example.com')).toBe(true); expect(validationUtils.isValidEmail('user@subdomain.example.com')).toBe(true); expect(validationUtils.isValidEmail('user@example.co.uk')).toBe(true); expect(validationUtils.isValidEmail('123@example.com')).toBe(true); }); }); describe('validateUsername', () => { it('should reject empty usernames', () => { const result = validationUtils.validateUsername(''); expect(result.valid).toBe(false); expect(result.message).toContain('required'); }); it('should reject usernames that are too short', () => { const result = validationUtils.validateUsername('ab'); expect(result.valid).toBe(false); expect(result.message).toContain('at least 3 characters'); }); it('should reject usernames that are too long', () => { const longUsername = 'a'.repeat(31); const result = validationUtils.validateUsername(longUsername); expect(result.valid).toBe(false); expect(result.message).toContain('at most 30 characters'); }); it('should reject usernames with invalid characters', () => { const result = validationUtils.validateUsername('user@name'); expect(result.valid).toBe(false); expect(result.message).toContain('invalid characters'); }); it('should accept valid usernames', () => { expect(validationUtils.validateUsername('username').valid).toBe(true); expect(validationUtils.validateUsername('user123').valid).toBe(true); expect(validationUtils.validateUsername('user_name').valid).toBe(true); expect(validationUtils.validateUsername('user-name').valid).toBe(true); expect(validationUtils.validateUsername('USERNAME').valid).toBe(true); }); it('should respect custom validation options', () => { // Custom options: min 5 chars, max 10 chars, only lowercase letters const options = { minLength: 5, maxLength: 10, allowedChars: /^[a-z]+$/ }; // Valid username with custom options const result1 = validationUtils.validateUsername('abcdef', options); expect(result1.valid).toBe(true); // Too short with custom options const result2 = validationUtils.validateUsername('abcd', options); expect(result2.valid).toBe(false); expect(result2.message).toContain('at least 5 characters'); // Too long with custom options const result3 = validationUtils.validateUsername('abcdefghijk', options); expect(result3.valid).toBe(false); expect(result3.message).toContain('at most 10 characters'); // Invalid characters with custom options const result4 = validationUtils.validateUsername('abcDEF', options); expect(result4.valid).toBe(false); expect(result4.message).toContain('invalid characters'); }); }); describe('validateName', () => { it('should reject empty names', () => { const result = validationUtils.validateName(''); expect(result.valid).toBe(false); expect(result.message).toContain('required'); }); it('should reject names that are too long', () => { const longName = 'a'.repeat(51); const result = validationUtils.validateName(longName); expect(result.valid).toBe(false); expect(result.message).toContain('at most 50 characters'); }); it('should accept valid names', () => { expect(validationUtils.validateName('John').valid).toBe(true); expect(validationUtils.validateName('John Doe').valid).toBe(true); expect(validationUtils.validateName('Jean-Claude').valid).toBe(true); expect(validationUtils.validateName('O\'Connor').valid).toBe(true); }); it('should respect the allowSpaces option', () => { const options = { allowSpaces: false }; // Valid name without spaces const result1 = validationUtils.validateName('John', options); expect(result1.valid).toBe(true); // Invalid name with spaces when spaces are not allowed const result2 = validationUtils.validateName('John Doe', options); expect(result2.valid).toBe(false); expect(result2.message).toContain('cannot contain spaces'); }); it('should respect custom length options', () => { const options = { minLength: 2, maxLength: 20 }; // Valid name with custom options const result1 = validationUtils.validateName('Jo', options); expect(result1.valid).toBe(true); // Too short with custom options const result2 = validationUtils.validateName('J', options); expect(result2.valid).toBe(false); expect(result2.message).toContain('at least 2 characters'); // Too long with custom options const longName = 'a'.repeat(21); const result3 = validationUtils.validateName(longName, options); expect(result3.valid).toBe(false); expect(result3.message).toContain('at most 20 characters'); }); }); describe('validatePhone', () => { it('should reject empty phone numbers', () => { const result = validationUtils.validatePhone(''); expect(result.valid).toBe(false); expect(result.message).toContain('required'); }); it('should reject phone numbers that are too short', () => { const result = validationUtils.validatePhone('123456'); expect(result.valid).toBe(false); expect(result.message).toContain('too short'); }); it('should accept valid phone numbers in various formats', () => { expect(validationUtils.validatePhone('1234567890').valid).toBe(true); expect(validationUtils.validatePhone('123-456-7890').valid).toBe(true); expect(validationUtils.validatePhone('(123) 456-7890').valid).toBe(true); expect(validationUtils.validatePhone('+1 123 456 7890').valid).toBe(true); }); }); describe('validateUrl', () => { it('should reject empty URLs', () => { const result = validationUtils.validateUrl(''); expect(result.valid).toBe(false); expect(result.message).toContain('required'); }); it('should reject invalid URLs', () => { expect(validationUtils.validateUrl('not-a-url').valid).toBe(false); // Note: In modern browsers, 'http:example.com' is actually considered valid by the URL constructor // It's interpreted as a URL with the 'http:' protocol and 'example.com' as the path expect(validationUtils.validateUrl('http:').valid).toBe(false); expect(validationUtils.validateUrl('://example.com').valid).toBe(false); }); it('should accept valid URLs', () => { expect(validationUtils.validateUrl('http://example.com').valid).toBe(true); expect(validationUtils.validateUrl('https://example.com').valid).toBe(true); expect(validationUtils.validateUrl('https://www.example.com').valid).toBe(true); expect(validationUtils.validateUrl('https://example.com/path').valid).toBe(true); expect(validationUtils.validateUrl('https://example.com/path?query=value').valid).toBe(true); expect(validationUtils.validateUrl('https://example.com:8080').valid).toBe(true); }); }); describe('validateDate', () => { it('should reject empty dates', () => { const result = validationUtils.validateDate(''); expect(result.valid).toBe(false); expect(result.message).toContain('required'); }); it('should reject invalid dates', () => { expect(validationUtils.validateDate('not-a-date').valid).toBe(false); expect(validationUtils.validateDate('2023-13-01').valid).toBe(false); // Invalid month // Note: JavaScript's Date constructor is lenient with invalid dates // '2023-02-30' gets converted to '2023-03-02' (rolls over to next month) // Let's use a different example that will definitely be invalid expect(validationUtils.validateDate('invalid-date-format').valid).toBe(false); }); it('should accept valid dates in various formats', () => { expect(validationUtils.validateDate('2023-01-15').valid).toBe(true); expect(validationUtils.validateDate(new Date('2023-01-15')).valid).toBe(true); expect(validationUtils.validateDate('2023/01/15').valid).toBe(true); expect(validationUtils.validateDate('01/15/2023').valid).toBe(true); }); it('should respect the minDate option', () => { const options = { minDate: new Date('2023-01-01') }; // Valid date after minDate const result1 = validationUtils.validateDate('2023-01-15', options); expect(result1.valid).toBe(true); // Invalid date before minDate const result2 = validationUtils.validateDate('2022-12-31', options); expect(result2.valid).toBe(false); expect(result2.message).toContain('on or after'); }); it('should respect the maxDate option', () => { const options = { maxDate: new Date('2023-12-31') }; // Valid date before maxDate const result1 = validationUtils.validateDate('2023-12-15', options); expect(result1.valid).toBe(true); // Invalid date after maxDate const result2 = validationUtils.validateDate('2024-01-01', options); expect(result2.valid).toBe(false); expect(result2.message).toContain('on or before'); }); it('should respect both minDate and maxDate options', () => { const options = { minDate: new Date('2023-01-01'), maxDate: new Date('2023-12-31') }; // Valid date within range const result1 = validationUtils.validateDate('2023-06-15', options); expect(result1.valid).toBe(true); // Invalid date before minDate const result2 = validationUtils.validateDate('2022-12-31', options); expect(result2.valid).toBe(false); // Invalid date after maxDate const result3 = validationUtils.validateDate('2024-01-01', options); expect(result3.valid).toBe(false); }); }); });