Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ The test suite includes comprehensive coverage of wallet connection handling:
- Retry behavior
- User interaction flows

- **Formatting Utility Tests** (`test/unit/format.test.js`)
- Currency amount formatting (`formatAmount`) with locale and currency symbol support
- Date and timestamp formatting (`formatDate`)
- Exchange rate formatting (`formatRate`)
- Input normalization (`formatCurrencyInput`)
- Percentage formatting (`formatPercent`)
- Grouped number formatting (`formatNumber`)
- Address and Stellar public key truncation (`shortenAddress`)

Integration tests cover send-money validation, successful transfer submission,
pending button behavior, duplicate-submission prevention, Transfers page
filter sync (search, status, and date-range presets such as last 7/30/90 days),
Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default [
languageOptions: {
globals: {
...globals.browser,
...globals.node,
...globals.es2021,
},
parserOptions: {
Expand Down
2 changes: 1 addition & 1 deletion src/services/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function connectWallet() {
* @param {object} payload - the transaction details to "sign"
* @returns {Promise<{signature: string}>}
*/
export function signTransaction(_payload) {
export function signTransaction() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
Expand Down
5 changes: 4 additions & 1 deletion test/integration/send-money-form.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import * as api from '../../src/services/api.js';
async function fillValidForm(user) {
await user.type(screen.getByLabelText(/recipient/i), 'amina@example.com');
await user.type(screen.getByLabelText(/amount/i), '15');
await user.selectOptions(screen.getByLabelText(/to/i), 'NGN');
await user.selectOptions(
screen.getByLabelText(/to/i, { selector: 'select' }),
'NGN',
);
}

function createdTransfer(payload) {
Expand Down
2 changes: 1 addition & 1 deletion test/services/wallet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('connectWallet', () => {

try {
await connectWallet();
} catch (err) {
} catch {
// Expected to throw
}

Expand Down
2 changes: 0 additions & 2 deletions test/touch-targets.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ function readCSS(filename) {
return fs.readFileSync(path.resolve(process.cwd(), 'src', filename), 'utf8');
}

const MIN_TARGET = '44px';

describe('touch target minimum sizes (44px)', () => {
describe('button elements', () => {
const css = readCSS(path.join('components', 'Button.css'));
Expand Down
97 changes: 96 additions & 1 deletion test/unit/format.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { describe, expect, it } from 'vitest';
import {
formatAmount,
formatCurrencyInput,
formatDate,
formatNumber,
formatPercent,
formatRate,
shortenAddress,
} from '../../src/utils/format.js';

describe('formatAmount', () => {
Expand All @@ -11,7 +15,6 @@ describe('formatAmount', () => {
});

it('respects an explicit locale for grouping and decimal separators', () => {
// French formatting uses a comma as the decimal separator, unlike en-US.
const result = formatAmount(1234.5, 'EUR', 'fr-FR');
expect(result).toContain('234,50');
expect(result).toContain('€');
Expand All @@ -26,6 +29,10 @@ describe('formatAmount', () => {
expect(formatAmount('not-a-number', 'USD')).toBe('$0.00');
});

it('handles numeric string inputs correctly', () => {
expect(formatAmount('100.5', 'USD')).toBe('$100.50');
});

it('defaults the locale argument itself when omitted, independent of the currency', () => {
expect(formatAmount(10, 'NGN')).toBe(
new Intl.NumberFormat('en-US', {
Expand All @@ -51,6 +58,67 @@ describe('formatDate', () => {

it('returns a placeholder for a missing value regardless of locale', () => {
expect(formatDate(null, 'fr-FR')).toBe('-');
expect(formatDate(undefined)).toBe('-');
expect(formatDate('')).toBe('-');
});

it('handles Date objects and numeric timestamps', () => {
const dateObj = new Date(value);
expect(formatDate(dateObj)).toBe('Jul 15, 2026');
expect(formatDate(dateObj.getTime())).toBe('Jul 15, 2026');
});
});

describe('formatRate', () => {
it('formats exchange rate as a "1 FROM = X TO" string with 4 decimals', () => {
expect(formatRate(294.62, 'USD', 'NGN')).toBe('1 USD = 294.6200 NGN');
expect(formatRate(0.0034, 'NGN', 'USD')).toBe('1 NGN = 0.0034 USD');
});

it('returns a placeholder when rate is null or undefined', () => {
expect(formatRate(null, 'USD', 'NGN')).toBe('-');
expect(formatRate(undefined, 'USD', 'NGN')).toBe('-');
});
});

describe('formatCurrencyInput', () => {
it('returns empty string for null, undefined, or empty values', () => {
expect(formatCurrencyInput(null)).toBe('');
expect(formatCurrencyInput(undefined)).toBe('');
expect(formatCurrencyInput('')).toBe('');
expect(formatCurrencyInput('.')).toBe('');
});

it('strips non-numeric characters and formats to two decimal places', () => {
expect(formatCurrencyInput('1,234.5')).toBe('1234.50');
expect(formatCurrencyInput('$100.5')).toBe('100.50');
expect(formatCurrencyInput('abc12.3xyz')).toBe('12.30');
});

it('handles clean number inputs', () => {
expect(formatCurrencyInput(15)).toBe('15.00');
expect(formatCurrencyInput('25.5')).toBe('25.50');
});

it('returns empty string for non-numeric input strings', () => {
expect(formatCurrencyInput('abc')).toBe('');
});
});

describe('formatPercent', () => {
it('formats fractional ratio as percentage string with 2 decimal places default', () => {
expect(formatPercent(0.005)).toBe('0.50%');
expect(formatPercent(0.15)).toBe('15.00%');
});

it('supports custom decimal places', () => {
expect(formatPercent(0.005, 1)).toBe('0.5%');
expect(formatPercent(0.12345, 3)).toBe('12.345%');
});

it('falls back to 0 for non-numeric or missing input', () => {
expect(formatPercent('not-a-number')).toBe('0.00%');
expect(formatPercent(null)).toBe('0.00%');
});
});

Expand All @@ -64,4 +132,31 @@ describe('formatNumber', () => {
expect(result).toContain('234,5');
expect(result).not.toBe(formatNumber(1234.5, 2, 'en-US'));
});

it('falls back to 0 for non-numeric inputs', () => {
expect(formatNumber('invalid')).toBe('0');
});
});

describe('shortenAddress', () => {
const address = 'GBQAZ7Z3X7DEMOPUBLICKEY4REMITFLOWWALLET123456789ABCDEF';

it('shortens long address with default head (6) and tail (4)', () => {
expect(shortenAddress(address)).toBe('GBQAZ7...CDEF');
});

it('allows custom head and tail lengths', () => {
expect(shortenAddress(address, 4, 3)).toBe('GBQA...DEF');
});

it('returns original value when string length is less than or equal to head + tail', () => {
expect(shortenAddress('SHORT')).toBe('SHORT');
expect(shortenAddress('1234567890')).toBe('1234567890');
});

it('returns "-" placeholder for empty, null, or undefined values', () => {
expect(shortenAddress('')).toBe('-');
expect(shortenAddress(null)).toBe('-');
expect(shortenAddress(undefined)).toBe('-');
});
});
Loading