This commit is contained in:
2026-02-16 19:34:35 +00:00
commit 9ab0940ca4
784 changed files with 41710 additions and 0 deletions
+331
View File
@@ -0,0 +1,331 @@
import { describe, expect, test, beforeAll, afterAll, spyOn } from 'bun:test';
import {
leftpad,
rightpad,
formatPercent,
formatTime,
formatDate,
formatDateTime,
formatCurrency,
formatNumber,
formatPercentage,
formatDiff,
daysAgo,
formatByThousands,
} from './formatters';
// Mock browser globals
const originalNavigator = globalThis.navigator;
beforeAll(() => {
// @ts-expect-error - mocking navigator for tests
globalThis.navigator = {
language: 'en-US',
languages: ['en-US'],
};
});
afterAll(() => {
globalThis.navigator = originalNavigator;
});
describe('leftpad', () => {
test('pads number with leading zeros', () => {
expect(leftpad(5, 3)).toBe('005');
expect(leftpad(42, 4)).toBe('0042');
expect(leftpad(123, 3)).toBe('123');
});
test('pads string with leading zeros', () => {
expect(leftpad('5', 3)).toBe('005');
expect(leftpad('ab', 4)).toBe('00ab');
});
test('handles value already at or exceeding size', () => {
expect(leftpad(12345, 3)).toBe('12345');
expect(leftpad('abc', 3)).toBe('abc');
});
test('handles default value of 0', () => {
expect(leftpad(undefined, 3)).toBe('000');
});
});
describe('rightpad', () => {
test('pads number with trailing zeros', () => {
expect(rightpad(5, 3)).toBe('500');
expect(rightpad(42, 4)).toBe('4200');
});
test('pads string with trailing zeros', () => {
expect(rightpad('5', 3)).toBe('500');
expect(rightpad('ab', 4)).toBe('ab00');
});
test('handles value already at or exceeding size', () => {
expect(rightpad(12345, 3)).toBe('12345');
});
test('handles default value of 0', () => {
expect(rightpad(undefined, 3)).toBe('000');
});
});
describe('formatPercent', () => {
test('formats number with 2 decimal places', () => {
expect(formatPercent(5.5)).toBe('05.50');
expect(formatPercent(12.34)).toBe('12.34');
expect(formatPercent(99.9)).toBe('99.90');
});
test('formats string numbers', () => {
expect(formatPercent('5.5')).toBe('05.50');
expect(formatPercent('12.34')).toBe('12.34');
});
test('handles whole numbers', () => {
expect(formatPercent(5)).toBe('05.00');
expect(formatPercent('12')).toBe('12.00');
});
test('handles zero', () => {
expect(formatPercent(0)).toBe('00.00');
expect(formatPercent('0')).toBe('00.00');
});
test('returns empty string for null/undefined', () => {
expect(formatPercent(null as unknown as number)).toBe('');
expect(formatPercent(undefined as unknown as number)).toBe('');
});
});
describe('formatPercentage', () => {
test('formats number as percentage string', () => {
expect(formatPercentage(5.5)).toBe('5.50%');
expect(formatPercentage(12.345)).toBe('12.35%');
expect(formatPercentage(0.1)).toBe('0.10%');
});
test('formats string numbers', () => {
expect(formatPercentage('5.5')).toBe('5.50%');
expect(formatPercentage('99')).toBe('99.00%');
});
test('returns default for falsy values', () => {
expect(formatPercentage(0)).toBe('00.00%');
expect(formatPercentage('')).toBe('00.00%');
});
});
describe('formatDiff', () => {
test('formats positive difference with plus sign', () => {
expect(formatDiff(5)).toBe('+5');
expect(formatDiff(10.7)).toBe('+11');
});
test('formats negative difference without extra sign', () => {
expect(formatDiff(-5)).toBe('-5');
expect(formatDiff(-10.7)).toBe('-11');
});
test('adds percent suffix when isPercent is true', () => {
expect(formatDiff(5, true)).toBe('+5%');
expect(formatDiff(-3, true)).toBe('-3%');
});
test('shows decimals when decimals is true', () => {
expect(formatDiff(5.55, false, true)).toBe('+5.55');
expect(formatDiff(-3.14, true, true)).toBe('-3.14%');
});
test('returns empty string for falsy values', () => {
expect(formatDiff(0)).toBe('');
expect(formatDiff('')).toBe('');
});
});
describe('daysAgo', () => {
const oneDay = 1000 * 60 * 60 * 24;
test('calculates days between two timestamps', () => {
const now = Date.now();
expect(daysAgo(now, now - oneDay)).toBe(1);
expect(daysAgo(now, now - oneDay * 5)).toBe(5);
});
test('order of arguments does not matter (absolute difference)', () => {
const now = Date.now();
const past = now - oneDay * 3;
expect(daysAgo(now, past)).toBe(3);
expect(daysAgo(past, now)).toBe(3);
});
test('same timestamp returns 0', () => {
const now = Date.now();
expect(daysAgo(now, now)).toBe(0);
});
test('partial days are rounded up', () => {
const now = Date.now();
const halfDayAgo = now - oneDay * 0.5;
expect(daysAgo(now, halfDayAgo)).toBe(1);
});
});
describe('formatByThousands', () => {
test('formats millions with M suffix', () => {
const result = formatByThousands(1500000);
expect(result).toContain('M');
expect(result).toContain('1');
});
test('formats thousands with K suffix', () => {
const result = formatByThousands(5000);
expect(result).toContain('K');
expect(result).toContain('5');
});
test('formats small numbers without suffix', () => {
const result = formatByThousands(500);
expect(result).not.toContain('K');
expect(result).not.toContain('M');
});
test('handles zero', () => {
const result = formatByThousands(0);
expect(result).toContain('0');
});
test('handles exactly 1000', () => {
const result = formatByThousands(1000);
expect(result).toContain('K');
});
test('handles exactly 1000000', () => {
const result = formatByThousands(1000000);
expect(result).toContain('M');
});
});
describe('formatTime', () => {
test('formats timestamp to time string', () => {
const mockToLocaleTimeString = spyOn(Date.prototype, 'toLocaleTimeString').mockReturnValue('10:30:00 AM');
const result = formatTime(1706180400000);
expect(result).toBe('10:30:00 AM');
mockToLocaleTimeString.mockRestore();
});
test('returns empty string for falsy values', () => {
expect(formatTime(0)).toBe('');
expect(formatTime('')).toBe('');
});
});
describe('formatDate', () => {
test('formats timestamp to date string', () => {
const mockToLocaleDateString = spyOn(Date.prototype, 'toLocaleDateString').mockReturnValue('1/25/2024');
const result = formatDate(1706180400000);
expect(result).toBe('1/25/2024');
mockToLocaleDateString.mockRestore();
});
test('returns empty string for falsy values', () => {
expect(formatDate(0)).toBe('');
expect(formatDate('')).toBe('');
});
});
describe('formatDateTime', () => {
test('formats timestamp to date and time string', () => {
const mockToLocaleDateString = spyOn(Date.prototype, 'toLocaleDateString').mockReturnValue('1/25/2024');
const mockToLocaleTimeString = spyOn(Date.prototype, 'toLocaleTimeString').mockReturnValue('10:30:00 AM');
const result = formatDateTime(1706180400000);
expect(result).toBe('1/25/2024 10:30:00 AM');
mockToLocaleDateString.mockRestore();
mockToLocaleTimeString.mockRestore();
});
test('returns empty string for falsy values', () => {
expect(formatDateTime(0)).toBe('');
expect(formatDateTime('')).toBe('');
});
});
describe('formatCurrency', () => {
const OriginalNumberFormat = Intl.NumberFormat;
afterAll(() => {
globalThis.Intl.NumberFormat = OriginalNumberFormat;
});
test('formats number as currency', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '$1,234.56';
};
const result = formatCurrency(1234.56, 'USD');
expect(result).toBe('$1,234.56');
});
test('handles string input', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '$99.00';
};
const result = formatCurrency('99', 'USD');
expect(result).toBe('$99.00');
});
test('handles zero and NaN', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '$0.00';
};
expect(formatCurrency(0, 'USD')).toBe('$0.00');
expect(formatCurrency(NaN, 'USD')).toBe('$0.00');
});
test('respects decimals parameter', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '€10.000';
};
const result = formatCurrency(10, 'EUR', 3);
expect(result).toBe('€10.000');
});
});
describe('formatNumber', () => {
const OriginalNumberFormat = Intl.NumberFormat;
afterAll(() => {
globalThis.Intl.NumberFormat = OriginalNumberFormat;
});
test('formats number with locale formatting', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '1,234,567';
};
const result = formatNumber(1234567);
expect(result).toBe('1,234,567');
});
test('handles string input', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '999';
};
const result = formatNumber('999');
expect(result).toBe('999');
});
test('handles zero and NaN', () => {
// @ts-expect-error - mocking Intl.NumberFormat
globalThis.Intl.NumberFormat = class {
format = () => '0';
};
expect(formatNumber(0)).toBe('0');
expect(formatNumber(NaN)).toBe('0');
});
});