Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : Add Currency Module : [ATLAS-26, ATLAS-27, ATLAS-28] #2

Merged
merged 39 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d8017ce
add currency module code
RgnDunes Oct 26, 2023
347c48b
remove unnecessary code
RgnDunes Oct 26, 2023
642d952
remove phoneNumber module
RgnDunes Oct 26, 2023
dceba28
remove phoneNumber import
RgnDunes Oct 26, 2023
10a60bd
revert: remove phone module
RgnDunes Oct 27, 2023
aafd8a4
add exception in formatAmount
RgnDunes Oct 30, 2023
1b435cb
Add exception in getCurrencySymbol
RgnDunes Oct 30, 2023
0413d05
minor refactor
RgnDunes Oct 30, 2023
8eb23ae
minor refactor
RgnDunes Oct 30, 2023
f8345c5
refactor type declarations
RgnDunes Oct 30, 2023
232484f
feat: Add UT's for Currency Module [ATLAS-23] (#5)
RgnDunes Nov 1, 2023
1a39206
resolve review comments
RgnDunes Nov 1, 2023
893b066
fix imports in test cases
RgnDunes Nov 1, 2023
704e87d
Merge remote-tracking branch 'origin/master' into currency-module
RgnDunes Nov 4, 2023
e5af556
add data file for country locales
RgnDunes Nov 4, 2023
93459ae
add formatAmountByParts
RgnDunes Nov 4, 2023
659071c
add formatAmountByParts.test file
RgnDunes Nov 4, 2023
cd21b48
refactor folder structure
RgnDunes Nov 4, 2023
cf56af2
Merge remote-tracking branch 'origin/master' into currency-module
RgnDunes Nov 7, 2023
3813b42
Merge remote-tracking branch 'origin/master' into currency-module
RgnDunes Nov 7, 2023
c42418e
resolve review comments
RgnDunes Nov 7, 2023
80bfbdc
refactor formatAmount to formatNumber
RgnDunes Nov 7, 2023
68f658a
prettier fixes
RgnDunes Nov 7, 2023
4fd44fc
prettier fixes
RgnDunes Nov 7, 2023
5f721eb
add missing test cases
RgnDunes Nov 7, 2023
1d89fbc
add missing test case for getCurrencyList
RgnDunes Nov 7, 2023
6d2df68
add UT for formatAmountByParts
RgnDunes Nov 8, 2023
2d2d7f2
add comment for failing test cases
RgnDunes Nov 8, 2023
f40a815
revert jest.config changes
RgnDunes Nov 8, 2023
7b1d35d
resolve review comments & add unit test cases
RgnDunes Nov 9, 2023
83102ed
remove only from test
RgnDunes Nov 9, 2023
137320f
fix failing test cases
RgnDunes Nov 9, 2023
93da57d
refactor test cases
RgnDunes Nov 9, 2023
aa1a88a
Merge remote-tracking branch 'origin/master' into currency-module
RgnDunes Nov 9, 2023
93c0144
refactor test cases
RgnDunes Nov 9, 2023
d489c17
add wityhBoundary return type
RgnDunes Nov 9, 2023
784b025
resolve review comments
RgnDunes Nov 9, 2023
d8dae6b
fix lint error
RgnDunes Nov 9, 2023
f0fdb3e
fix lint error
RgnDunes Nov 9, 2023
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
3,484 changes: 0 additions & 3,484 deletions package-lock.json

This file was deleted.

6 changes: 4 additions & 2 deletions src/common/errorBoundary/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { withErrorBoundary, I18nifyError } from '../';
describe('withErrorBoundary', () => {
it('should call the wrapped function and not throw an error if there are no exceptions', () => {
const wrappedFunction = jest.fn();
const wrappedUtilityFn = withErrorBoundary(wrappedFunction);
const wrappedUtilityFn =
withErrorBoundary<typeof wrappedFunction>(wrappedFunction);

wrappedUtilityFn('arg1', 'arg2');

Expand All @@ -17,7 +18,8 @@ describe('withErrorBoundary', () => {
throw new Error(exceptionMessage);
};

const wrappedUtilityFn = withErrorBoundary(throwingFunction);
const wrappedUtilityFn =
withErrorBoundary<typeof throwingFunction>(throwingFunction);

expect(() => wrappedUtilityFn()).toThrow(I18nifyError);
});
Expand Down
6 changes: 3 additions & 3 deletions src/common/errorBoundary/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export class I18nifyError extends Error {
* @param fn utility that is wrapped in error boundary
* @returns {Function} returns the function wrapped in try/catch block
*/
export const withErrorBoundary = <T extends (...args: unknown[]) => void>(
export const withErrorBoundary = <T extends (...args: unknown[]) => unknown>(
fn: T,
): ((...args: Parameters<T>) => void) => {
): ((...args: Parameters<T>) => ReturnType<T>) => {
return function (...rest: Parameters<T>) {
try {
fn.call(this, ...rest);
return fn.call(this, ...rest);
} catch (err) {
// Currently, we are throwing the error as it is to consumers.
// In the future, this can be modified as per our requirement, like an error logging service.
Expand Down
120 changes: 120 additions & 0 deletions src/modules/currency/__tests__/formatNumber.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { getLocale } from '../../shared/utils/getLocale';
import formatNumber from '../formatNumber';

const nbsp = String.fromCharCode(160);
const nnsp = String.fromCharCode(8239);

describe('formatNumber', () => {
it('should format the amount with default options', () => {
const result = formatNumber('1000.5', { currency: 'USD' });
expect(result).toBe('$1,000.50');
});

it('should format the amount with custom locale and currency display', () => {
const result = formatNumber('1500', {
currency: 'EUR',
locale: 'fr-FR',
intlOptions: {
currencyDisplay: 'code',
},
});

expect(result).toBe(`1${nnsp}500,00${nbsp}EUR`);
});

it('should format the amount without currency symbol', () => {
const result = formatNumber('750.75');
expect(result).toBe('750.75');
});

it('should format the amount with narrow currency symbol', () => {
const result = formatNumber('5000', {
currency: 'JPY',
intlOptions: {
currencyDisplay: 'narrowSymbol',
},
});
expect(result).toBe('¥5,000');
});

it('should format the amount using the browser locale when no custom locale is provided', () => {
const result = formatNumber('2000', {
currency: 'CAD',
});
const expectedLocale = getLocale();
const formattedAmount = new Intl.NumberFormat(expectedLocale, {
style: 'currency',
currency: 'CAD',
}).format(2000);
expect(result).toBe(formattedAmount);
});

it('should handle invalid inputs gracefully', () => {
expect(() =>
formatNumber('invalid-amount', {
currency: 'USD',
}),
).toThrow('Parameter `amount` is not a number!');
});

it('should format a negative amount', () => {
const result = formatNumber('-500', { currency: 'USD' });
expect(result).toBe('-$500.00');
});

it('should format zero as "0.00"', () => {
const result = formatNumber(0, { currency: 'USD' });
expect(result).toBe('$0.00');
});

it('should format with custom minimum and maximum fraction digits', () => {
const result = formatNumber('42.12345', {
currency: 'USD',
intlOptions: {
minimumFractionDigits: 2,
maximumFractionDigits: 3,
},
});
expect(result).toBe('$42.123');
});

it('should format with all default options', () => {
const result = formatNumber(12345.6789);
expect(result).toBe('12,345.679');
});

it('should throw error with thousands separators', () => {
expect(() => formatNumber('1,234,567.89', { currency: 'USD' })).toThrow(
'Parameter `amount` is not a number!',
);
});

it('should throw error with a different decimal separator', () => {
expect(() =>
formatNumber('1000,5', {
currency: 'USD',
intlOptions: { useGrouping: false },
}),
).toThrow('Parameter `amount` is not a number!');
});

it('should handle extremely large numbers with precision', () => {
const input = '1234567890123456.7890123456789012345678901234567890123456';
const result = formatNumber(input, { currency: 'USD' });
expect(result).toBe('$1,234,567,890,123,456.80');
});

it('should handle custom currency symbol and placement', () => {
const result = formatNumber('1000', {
currency: 'XYZ',
intlOptions: {
style: 'currency',
currencyDisplay: 'symbol',
currencySign: 'accounting',
},
});

const expected = `XYZ${nbsp}1,000.00`;
expect(result).toBe(expected);
});
});
94 changes: 94 additions & 0 deletions src/modules/currency/__tests__/formatNumberByParts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import formatNumberByParts from '../formatNumberByParts';

describe('formatNumberByParts', () => {
it('should format the amount correctly for a given currency', () => {
const result = formatNumberByParts(12345.67, {
currency: 'USD',
locale: 'en-US',
});

expect(result).toEqual({
currencySymbol: '$',
integerValue: '12,345',
decimalValue: '67',
separator: '.',
symbolAtFirst: true,
});
});

it('should throw error for non-numeric input', () => {
expect(() =>
formatNumberByParts('not a number', {
currency: 'USD',
locale: 'en-US',
}),
).toThrow('Error: Parameter `amount` is not a number!');
});

it('should use the default locale if locale is not provided', () => {
const result = formatNumberByParts(12345.67, {
currency: 'USD',
});

expect(result).toBeDefined();
});

it('should handle invalid currency code', () => {
const result = formatNumberByParts(12345.67, {
currency: 'XYZ',
locale: 'en-US',
});
expect(result).toEqual({
currencySymbol: 'XYZ',
decimalValue: '67',
integerValue: '12,345',
separator: '.',
symbolAtFirst: true,
});
});

it('should handle different locales', () => {
const result = formatNumberByParts(12345.67, {
currency: 'EUR',
locale: 'fr-FR',
});

expect(result).toEqual({
currencySymbol: '€',
decimalValue: '67',
integerValue: '12 345',
separator: ',',
symbolAtFirst: false,
});
});

it('should handle a currency with symbol at the beginning', () => {
const result = formatNumberByParts(12345.67, {
currency: 'JPY',
locale: 'ja-JP',
});

expect(result).toEqual({
currencySymbol: '¥',
decimalValue: '',
integerValue: '12,346',
separator: '',
symbolAtFirst: true,
});
});

it('should handle a currency with decimal value and symbol at the end', () => {
const result = formatNumberByParts(12345.67, {
currency: 'OMR',
locale: 'ar-OM',
});

expect(result).toEqual({
currencySymbol: 'ر.ع.',
decimalValue: '٦٧٠',
integerValue: '١٢٬٣٤٥',
separator: '٫',
symbolAtFirst: false,
});
});
});
26 changes: 26 additions & 0 deletions src/modules/currency/__tests__/getCurrencyList.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import getCurrencyList from '../getCurrencyList';
import { CURRENCIES } from '../data/currencies';
describe('getCurrencyList', () => {
it('should return the correct currency list', () => {
const currencyList = getCurrencyList();
expect(currencyList).toEqual(CURRENCIES);
RgnDunes marked this conversation as resolved.
Show resolved Hide resolved
});

it("check properties 'symbol' and 'name' for a sample currency", () => {
const currencyList = getCurrencyList();
const sampleCurrencyCode = 'USD';

expect(currencyList[sampleCurrencyCode]).toHaveProperty('symbol');
expect(currencyList[sampleCurrencyCode]).toHaveProperty('name');
});

it("check the values of 'symbol' and 'name' properties for a sample currency", () => {
const currencyList = getCurrencyList();
const sampleCurrencyCode = 'USD';
const sampleCurrency = currencyList[sampleCurrencyCode];

// Assert that the 'symbol' and 'name' properties have the expected values
expect(sampleCurrency.symbol).toBe(CURRENCIES[sampleCurrencyCode].symbol);
expect(sampleCurrency.name).toBe(CURRENCIES[sampleCurrencyCode].name);
});
});
24 changes: 24 additions & 0 deletions src/modules/currency/__tests__/getCurrencySymbol.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import getCurrencySymbol from '../getCurrencySymbol';

describe('getCurrencySymbol', () => {
it('should return the correct symbol for a valid currency code', () => {
const currencyCode = 'USD';
const expectedSymbol = '$';
const symbol = getCurrencySymbol(currencyCode);
expect(symbol).toBe(expectedSymbol);
});

it('should throw Error for an invalid currency code', () => {
const currencyCode = 'XYZ'; // An invalid code
expect(() => getCurrencySymbol(currencyCode)).toThrow(
'Invalid currencyCode!',
);
});

it('should throw Error for an empty string', () => {
const currencyCode = '';
expect(() => getCurrencySymbol(currencyCode)).toThrow(
'Invalid currencyCode!',
);
});
});
Loading
Loading