import { InvalidPhoneNumberError } from './invalid-phone-number.error'; import { PhoneNumber } from './phone-number'; describe('PhoneNumber', () => { describe('create', () => { it('creates a valid E.164 number and stores the canonical form', () => { const phone = PhoneNumber.create('+6281234567890'); expect(phone.value).toBe('+6281234567890'); }); it('trims surrounding whitespace', () => { const phone = PhoneNumber.create(' +6281234567890 '); expect(phone.value).toBe('+6281234567890'); }); it('normalizes formatted international numbers to E.164', () => { const phone = PhoneNumber.create('+62 812 3456 7890'); expect(phone.value).toBe('+6281234567890'); }); it('rejects numbers missing a leading +', () => { expect(() => PhoneNumber.create('6281234567890')).toThrow( InvalidPhoneNumberError, ); }); it('rejects Indonesian national format', () => { expect(() => PhoneNumber.create('081234567890')).toThrow( InvalidPhoneNumberError, ); }); it('rejects international prefix without +', () => { expect(() => PhoneNumber.create('006281234567890')).toThrow( InvalidPhoneNumberError, ); }); it('rejects empty string', () => { expect(() => PhoneNumber.create('')).toThrow(InvalidPhoneNumberError); }); it('rejects whitespace-only input', () => { expect(() => PhoneNumber.create(' ')).toThrow(InvalidPhoneNumberError); }); it('rejects non-string input', () => { expect(() => PhoneNumber.create(null as unknown as string)).toThrow( InvalidPhoneNumberError, ); expect(() => PhoneNumber.create(123 as unknown as string)).toThrow( InvalidPhoneNumberError, ); }); it('rejects non-digit junk', () => { expect(() => PhoneNumber.create('+not-a-phone')).toThrow( InvalidPhoneNumberError, ); }); it('rejects invalid E.164-looking values', () => { expect(() => PhoneNumber.create('+999')).toThrow(InvalidPhoneNumberError); }); it('rejects trailing junk after a valid number', () => { expect(() => PhoneNumber.create('+6281234567890abc')).toThrow( InvalidPhoneNumberError, ); }); it('rejects numbers with extensions', () => { expect(() => PhoneNumber.create('+6281234567890 ext. 123')).toThrow( InvalidPhoneNumberError, ); expect(() => PhoneNumber.create('+62 812-3456-7890 x99')).toThrow( InvalidPhoneNumberError, ); }); it('throws a generic message that does not echo the input', () => { expect(() => PhoneNumber.create('081234567890')).toThrow( 'Invalid phone number', ); }); }); describe('equals', () => { it('returns true for the same canonical value', () => { const a = PhoneNumber.create('+6281234567890'); const b = PhoneNumber.create('+62 812 3456 7890'); expect(a.equals(b)).toBe(true); }); it('returns false for different numbers', () => { const a = PhoneNumber.create('+6281234567890'); const b = PhoneNumber.create('+14155552671'); expect(a.equals(b)).toBe(false); }); it('returns false for null or undefined', () => { const phone = PhoneNumber.create('+6281234567890'); expect(phone.equals(null as unknown as PhoneNumber)).toBe(false); expect(phone.equals(undefined as unknown as PhoneNumber)).toBe(false); }); }); describe('serialization', () => { it('toString returns E.164', () => { const phone = PhoneNumber.create('+6281234567890'); expect(phone.toString()).toBe('+6281234567890'); }); it('toJSON returns E.164', () => { const phone = PhoneNumber.create('+6281234567890'); expect(phone.toJSON()).toBe('+6281234567890'); expect(JSON.stringify({ phone })).toBe('{"phone":"+6281234567890"}'); }); }); describe('immutability', () => { it('is frozen after creation', () => { const phone = PhoneNumber.create('+6281234567890'); expect(Object.isFrozen(phone)).toBe(true); }); it('cannot be constructed with new from outside', () => { expect( () => new (PhoneNumber as unknown as new (value: string) => PhoneNumber)( '+6281234567890', ), ).toThrow(); }); }); });